CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-streamlit

The fastest way to build and share data apps

Overview
Eval results
Files

custom-components.mddocs/

Custom Components

Framework for creating and using custom HTML/JavaScript components. This enables extension of Streamlit's built-in widget set with reusable interactive elements that can integrate frontend technologies like React, Vue, or vanilla JavaScript.

Capabilities

Component Declaration

Create custom components that can be reused across applications.

def declare_component(
    name: str,
    path: str = None,
    url: str = None
) -> ComponentCallable:
    """
    Declare a custom Streamlit component.
    
    Parameters:
    - name: Component name (must be unique)
    - path: Path to component's build directory (for local development)
    - url: URL of component (for production deployment)
    
    Returns:
    ComponentCallable: Function that can be called to render the component
    """

Usage Example

import streamlit as st
import streamlit.components.v1 as components

# Declare a custom component during development
_my_component = components.declare_component(
    "my_component",
    path="./my_component/build"  # Path to built component
)

# Declare component for production
_my_component = components.declare_component(
    "my_component", 
    url="https://my-component-cdn.com/my_component"
)

# Use the component
def my_component(message, key=None):
    """Wrapper function for the custom component."""
    return _my_component(
        message=message,
        key=key,
        default=None
    )

# Call the component
result = my_component("Hello from custom component!", key="my_comp")
if result:
    st.write(f"Component returned: {result}")

HTML Rendering

Render raw HTML content within Streamlit apps.

def html(body: str, width: int = None, height: int = None, scrolling: bool = False) -> None:
    """
    Render HTML content in an iframe.
    
    Parameters:
    - body: HTML string to render
    - width: Width of the iframe in pixels (None for auto)
    - height: Height of the iframe in pixels (None for auto)
    - scrolling: Whether to allow scrolling in the iframe
    """

Usage Example

import streamlit as st
import streamlit.components.v1 as components

# Simple HTML rendering
components.html("""
<h2 style="color: blue;">Custom HTML Content</h2>
<p>This is rendered as HTML with custom styling.</p>
<button onclick="alert('Button clicked!')">Click Me</button>
""", height=150)

# Interactive HTML with JavaScript
components.html("""
<div id="my-component" style="padding: 20px; border: 1px solid #ccc;">
    <h3>Interactive Counter</h3>
    <button id="increment">+</button>
    <span id="count">0</span>
    <button id="decrement">-</button>
</div>

<script>
let count = 0;
const countDisplay = document.getElementById('count');
const incrementBtn = document.getElementById('increment');
const decrementBtn = document.getElementById('decrement');

incrementBtn.addEventListener('click', () => {
    count++;
    countDisplay.textContent = count;
});

decrementBtn.addEventListener('click', () => {
    count--;
    countDisplay.textContent = count;
});
</script>
""", height=200)

# HTML with external libraries
components.html("""
<div id="chart"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const data = [4, 8, 15, 16, 23, 42];
const svg = d3.select("#chart")
    .append("svg")
    .attr("width", 400)
    .attr("height", 200);

svg.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", (d, i) => i * 60)
    .attr("y", d => 200 - d * 4)
    .attr("width", 50)
    .attr("height", d => d * 4)
    .attr("fill", "steelblue");
</script>
""", height=220)

IFrame Embedding

Embed external web content through iframes.

def iframe(src: str, width: int = None, height: int = None, scrolling: bool = False) -> None:
    """
    Embed an external URL in an iframe.
    
    Parameters:
    - src: URL to embed
    - width: Width of the iframe in pixels (None for auto)
    - height: Height of the iframe in pixels (None for auto)
    - scrolling: Whether to allow scrolling in the iframe
    """

Usage Example

import streamlit as st
import streamlit.components.v1 as components

# Embed external website
st.subheader("Embedded Web Content")
components.iframe("https://example.com", height=400)

# Embed interactive dashboard
components.iframe(
    "https://public.tableau.com/views/Dashboard/Sheet1",
    width=800,
    height=600,
    scrolling=True
)

# Embed map
components.iframe(
    "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d...",
    height=300
)

# Embed video
components.iframe(
    "https://www.youtube.com/embed/VIDEO_ID",
    height=315
)

Advanced Component Development

Structure for building sophisticated custom components.

Component Development Example

# my_component.py
import streamlit as st
import streamlit.components.v1 as components
import os

# Component declaration
_component_func = components.declare_component(
    "my_advanced_component",
    path=os.path.join(os.path.dirname(__file__), "frontend", "build"),
)

def my_advanced_component(
    data,
    config=None,
    theme="light",
    key=None
):
    """
    Create an advanced custom component.
    
    Parameters:
    - data: Data to pass to the component
    - config: Configuration dictionary  
    - theme: UI theme ("light" or "dark")
    - key: Unique key for the component
    
    Returns:
    Component return value (user interactions, selections, etc.)
    """
    if config is None:
        config = {}
    
    # Pass data to component
    component_value = _component_func(
        data=data,
        config=config,
        theme=theme,
        key=key,
        default=None
    )
    
    return component_value

# Usage in Streamlit app
if __name__ == "__main__":
    st.title("Advanced Component Demo")
    
    # Sample data
    chart_data = {
        "labels": ["Jan", "Feb", "Mar", "Apr", "May"],
        "datasets": [{
            "label": "Sales",
            "data": [12, 19, 3, 5, 2],
            "backgroundColor": "rgba(75,192,192,0.6)"
        }]
    }
    
    # Component configuration
    config = {
        "responsive": True,
        "maintainAspectRatio": False,
        "plugins": {
            "legend": {"display": True},
            "title": {"display": True, "text": "Sales Chart"}
        }
    }
    
    # Render component
    result = my_advanced_component(
        data=chart_data,
        config=config,
        theme="light",
        key="sales_chart"
    )
    
    if result:
        st.write("User interaction:", result)

Frontend Component Structure

// frontend/src/MyComponent.tsx (React example)
import React, { useEffect, useState } from "react"
import { Streamlit, withStreamlitConnection } from "streamlit-component-lib"

interface ComponentProps {
  args: {
    data: any
    config: any
    theme: string
  }
}

const MyComponent: React.FC<ComponentProps> = ({ args }) => {
  const { data, config, theme } = args
  const [selectedValue, setSelectedValue] = useState<string>("")

  useEffect(() => {
    // Auto-resize component height
    Streamlit.setFrameHeight()
  })

  const handleClick = (value: string) => {
    setSelectedValue(value)
    // Send data back to Streamlit
    Streamlit.setComponentValue(value)
  }

  return (
    <div className={`component ${theme}`}>
      <h3>Custom Interactive Component</h3>
      <div className="data-display">
        {data.labels.map((label: string, index: number) => (
          <button
            key={index}
            onClick={() => handleClick(label)}
            className={selectedValue === label ? "selected" : ""}
          >
            {label}: {data.datasets[0].data[index]}
          </button>
        ))}
      </div>
      {selectedValue && (
        <p>Selected: {selectedValue}</p>
      )}
    </div>
  )
}

export default withStreamlitConnection(MyComponent)

Component Communication

Bidirectional communication between Streamlit and custom components.

Usage Example

import streamlit as st
import streamlit.components.v1 as components

# Component that sends data back to Streamlit
components.html("""
<div>
    <h3>Data Entry Component</h3>
    <input type="text" id="user-input" placeholder="Enter text...">
    <button onclick="sendData()">Send to Streamlit</button>
</div>

<script>
function sendData() {
    const input = document.getElementById('user-input');
    const data = {
        text: input.value,
        timestamp: new Date().toISOString()
    };
    
    // Send data back to Streamlit
    window.parent.postMessage({
        type: 'streamlit:setComponentValue',
        value: data
    }, '*');
}
</script>
""", height=150, key="data_entry")

# Receive data from component
result = st.session_state.get("data_entry")
if result:
    st.write(f"Received from component: {result}")

Component Integration Patterns

Common patterns for integrating custom components.

Usage Example

import streamlit as st
import streamlit.components.v1 as components
import json

# Pattern 1: Data Visualization Component
def render_custom_chart(data, chart_type="bar"):
    """Render a custom chart component."""
    chart_html = f"""
    <div id="custom-chart"></div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
    const ctx = document.getElementById('custom-chart').getContext('2d');
    new Chart(ctx, {{
        type: '{chart_type}',
        data: {json.dumps(data)},
        options: {{
            responsive: true,
            plugins: {{
                title: {{
                    display: true,
                    text: 'Custom Chart'
                }}
            }}
        }}
    }});
    </script>
    """
    
    components.html(chart_html, height=400)

# Pattern 2: Form Component
def custom_form_component():
    """Create a custom form with validation."""
    form_html = """
    <form id="custom-form" style="padding: 20px;">
        <div style="margin-bottom: 15px;">
            <label>Name:</label>
            <input type="text" id="name" required>
        </div>
        <div style="margin-bottom: 15px;">
            <label>Email:</label>
            <input type="email" id="email" required>
        </div>
        <div style="margin-bottom: 15px;">
            <label>Message:</label>
            <textarea id="message" rows="4" required></textarea>
        </div>
        <button type="submit">Submit</button>
    </form>
    
    <script>
    document.getElementById('custom-form').addEventListener('submit', function(e) {
        e.preventDefault();
        const formData = {
            name: document.getElementById('name').value,
            email: document.getElementById('email').value,
            message: document.getElementById('message').value
        };
        
        window.parent.postMessage({
            type: 'streamlit:setComponentValue',
            value: formData
        }, '*');
    });
    </script>
    """
    
    return components.html(form_html, height=300, key="custom_form")

# Use the patterns
st.title("Custom Component Patterns")

# Chart pattern
chart_data = {
    "labels": ["A", "B", "C", "D"],
    "datasets": [{
        "label": "Dataset 1",
        "data": [12, 19, 3, 5],
        "backgroundColor": ["#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0"]
    }]
}

render_custom_chart(chart_data, "doughnut")

# Form pattern
form_result = custom_form_component()
if form_result:
    st.success("Form submitted!")
    st.json(form_result)

Component Development Guidelines

  • Component Structure: Use modern frontend frameworks (React, Vue, Angular)
  • Communication: Use Streamlit.setComponentValue() to send data back
  • Responsive Design: Components should work on different screen sizes
  • State Management: Handle component state independently from Streamlit
  • Performance: Minimize component re-renders and data transfers
  • Security: Sanitize user inputs and validate data
  • Documentation: Provide clear usage examples and API documentation

Component Deployment

  • Development: Use local file paths with declare_component(path=...)
  • Production: Host built components and use declare_component(url=...)
  • Distribution: Publish components as Python packages with built frontend assets
  • Versioning: Version components to ensure compatibility

Install with Tessl CLI

npx tessl i tessl/pypi-streamlit@1.16.0

docs

caching-config.md

charts-media.md

custom-components.md

display-elements.md

index.md

input-widgets.md

layout-containers.md

tile.json