The fastest way to build and share data apps
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.
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
"""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}")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
"""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)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
"""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
)Structure for building sophisticated custom components.
# 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/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)Bidirectional communication between Streamlit and custom components.
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}")Common patterns for integrating custom components.
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)Streamlit.setComponentValue() to send data backdeclare_component(path=...)declare_component(url=...)Install with Tessl CLI
npx tessl i tessl/pypi-streamlit@1.16.0