Quickstart examples
Get started with custom components v2 through these practical examples. Each example introduces a new concept to progressively build your understanding. To highlight each concept, the code on this page shows either a portion of the component example or a simplified version of it. Follow the links below each example to see the complete code for that example, including explanations.
Two-step component process
Creating and using a custom component involves two distinct steps:
- Registration: Define your component's HTML, CSS, and JavaScript with
st.components.v2.component(). - Mounting: Mount a specific instance of your component to your app's frontend using the
ComponentRenderercreated during registration.
For detailed explanations, see Component registration and Component mounting.
Hello world
This is a minimal static component that displays "Hello, World!" using the app's primary theme color. This component introduces the following concepts:
- Component registration with HTML and CSS using
st.components.v2.component() - Theme integration using CSS custom properties
- Mounting a component by calling the
ComponentRenderer
Rich data
This is a component that receives various data types from Python. This component introduces the following concepts:
- Passing data from Python via the
dataparameter and accessing it in JavaScript - Automatic dataframe and JSON serialization
- Passing an image as a Base64-encoded string
- Using a placeholder in the component's HTML and dynamically updating it with received data
Simple button
This is an interactive button that sends events to Python. This component introduces the following concepts:
- Component registration with HTML, CSS, and JavaScript
- One-time trigger values sent from JavaScript with
setTriggerValue() - Callback functions using the
on_<trigger>_changenaming pattern - Accessing trigger values from the component's return object
Simple checkbox
This is a simple checkbox that reports a stateful value to Python. This component introduces the following concepts:
- Persistent state values sent from JavaScript with
setStateValue() - Callback functions with the
on_<state>_changenaming pattern - Initializing a stateful component with the
dataanddefaultparameters - Using font from the app's theme
- Accessing state values from the component's return object
Interactive counter
This is a counter with increment, decrement, and reset functionality. This component introduces the following concepts:
- Combining state and trigger values in one component
- Multiple event handlers
<div class="counter">
<h3>Count: <span id="display">0</span></h3>
<div class="buttons">
<button id="decrement">-1</button>
<button id="increment">+1</button>
<button id="reset">Reset</button>
</div>
</div>
export default function ({ parentElement, setStateValue, setTriggerValue }) {
const incrementBtn = parentElement.querySelector("#increment");
const decrementBtn = parentElement.querySelector("#decrement");
const resetBtn = parentElement.querySelector("#reset");
let count = 0;
decrementBtn.onclick = () => {
count--;
setStateValue("count", count); // Persistent state
};
incrementBtn.onclick = () => {
count++;
setStateValue("count", count); // Persistent state
};
resetBtn.onclick = () => {
count = 0;
setTriggerValue("reset", true); // One-time event
setStateValue("count", 0);
};
}
Text input
This is a text input component that demonstrates full bidirectional communication, including programmatic updates from Python. This component introduces the following concepts:
- Mounting a component with a key and reading component state from Session State
- Wrapping a component's raw mounting command to create a user-friendly mounting command
- Programmatic updates from Python via the
dataparameter - Syncing frontend state without interrupting user input
const input = parentElement.querySelector("input");
// Sync input value with data from Python
if (input.value !== data.value) {
input.value = data.value ?? "";
}
Danger button
This is a hold-to-confirm button with frontend validation and visual feedback. This component introduces the following concepts:
- Frontend validation before sending data to Python
- Timed interactions with
requestAnimationFrame() - Visual feedback with CSS animations and transitions
- Rate limiting with cooldown periods
- Touch events for mobile support
- Layout control using the
widthparameter - Cleanup functions for event listeners
function startHold() {
startTime = Date.now();
animationFrame = requestAnimationFrame(updateProgress);
}
function updateProgress() {
const progressPercent = Math.min(elapsed / HOLD_DURATION, 1);
if (progressPercent >= 1) {
setTriggerValue("confirmed", true); // Only after 2 seconds
} else {
animationFrame = requestAnimationFrame(updateProgress);
}
}
Radial menu
This is a circular selection menu demonstrating state values for persistent selections. This component introduces the following concepts:
- CSS custom properties for dynamic positioning (
--i,--total) - A fixed-position backdrop for click-outside behavior
- Complex animations with CSS transitions
// Dynamic element creation
Object.entries(options).forEach(([value, icon], index) => {
const button = document.createElement("button");
button.style.setProperty("--i", index);
button.style.setProperty("--total", Object.keys(options).length);
// ...
});
What's next?
Now that you've seen these examples:
- Learn the fundamentals in Component registration and Component mounting.
- Understand State versus trigger values for advanced interactions.
- Explore Theming and styling to make beautiful components.
- Build complex projects with Package-based components.
Still have questions?
Our forums are full of helpful information and Streamlit experts.