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.

Creating and using a custom component involves two distinct steps:

  1. Registration: Define your component's HTML, CSS, and JavaScript with st.components.v2.component().
  2. Mounting: Mount a specific instance of your component to your app's frontend using the ComponentRenderer created during registration.

For detailed explanations, see Component registration and Component mounting.

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
import streamlit as st hello_component = st.components.v2.component( name="hello_world", html="<h2>Hello, World!</h2>", css="h2 { color: var(--st-primary-color); }", ) hello_component()
arrow_forwardView the full example

This is a component that receives various data types from Python. This component introduces the following concepts:

  • Passing data from Python via the data parameter 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
data_component = st.components.v2.component( "rich_data", html="""<div id="data-container">Loading data...</div>""", js=""" export default function({ data, parentElement }) { const container = parentElement.querySelector("#data-container"); const df = data.df; const userInfo = data.user_info; const imgBase64 = data.image_base64; container.innerHTML = ` <h4>Dataframe: ${df}</h4> <h4>User Info: ${userInfo.name}</h4> <img src="data:image/png;base64,${imgBase64}" style="width: 25%;" /> `; } """, ) data_component( data={ "df": df, # Arrow-serializable "user_info": {"name": "Alice"}, # JSON-serializable "image_base64": img_base64 # Base64 string } )
arrow_forwardView the full example

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>_change naming pattern
  • Accessing trigger values from the component's return object
import streamlit as st def handle_button_click(): st.session_state.click_count += 1 st.session_state.setdefault("click_count", 0) button_component = st.components.v2.component( "simple_button", html="""<button id="btn">Click me</button>""", css="""button { background-color: var(--st-primary-color); }""", js=""" export default function(component) { const { setTriggerValue, parentElement } = component; parentElement.querySelector("button").onclick = () => { setTriggerValue("action", "button_clicked"); }; } """, ) result = button_component(on_action_change=handle_button_click) st.write(result.action) st.write(f"Total clicks: {st.session_state.click_count}")
arrow_forwardView the full example

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>_change naming pattern
  • Initializing a stateful component with the data and default parameters
  • Using font from the app's theme
  • Accessing state values from the component's return object
import streamlit as st checkbox_component = st.components.v2.component( "simple_checkbox", html=""" <label class="checkbox-container"> <input type="checkbox" id="checkbox" /> <span>Enable feature</span> </label> """, css=""" .checkbox-container { gap: 0.5rem; font-family: var(--st-font); color: var(--st-text-color); } """, js=""" export default function({ parentElement, data, setStateValue }) { const checkbox = parentElement.querySelector("#checkbox"); checkbox.checked = data?.checked ?? false; checkbox.onchange = () => { setStateValue("checked", checkbox.checked); }; } """, ) initial_state = False result = checkbox_component( data={"checked": initial_state}, default={"checked": initial_state}, on_checked_change=lambda: None, ) st.write(f"Current state: {'Enabled' if result.checked else 'Disabled'}")
arrow_forwardView the full example

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); }; }
arrow_forwardView the full example

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 data parameter
  • Syncing frontend state without interrupting user input
def my_component_wrapper( label, *, default="", key=None, on_change=lambda: None ): # Read current state from Session State component_state = st.session_state.get(key, {}) value = component_state.get("value", default) # Pass current value to component data = {"label": label, "value": value} result = my_component( data=data, default={"value": value}, key=key, on_value_change=on_change, ) return result
const input = parentElement.querySelector("input"); // Sync input value with data from Python if (input.value !== data.value) { input.value = data.value ?? ""; }
arrow_forwardView the full example

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 width parameter
  • 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); } }
result = danger_button( on_confirmed_change=handle_deletion, width="content" # Layout control )
arrow_forwardView the full example

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)
  • Document-level event listeners
  • Complex animations with CSS transitions
result = radial_menu( data={"options": options, "selection": "burger"}, default={"selection": "burger"}, # Avoids initial rerun on_selection_change=lambda: None, )
// 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); // ... });
arrow_forwardView the full example

Now that you've seen these examples:

forum

Still have questions?

Our forums are full of helpful information and Streamlit experts.