Bidirectional communication
Custom components v2 supports full bidirectional communication between your Python backend and JavaScript frontend. This enables you to:
- Send data from Python to your component via the
dataparameter - Receive user actions in Python from your component's state and trigger values
- Create feedback loops where Python updates the component programmatically
The basic concepts of component communication are introduced in the Component mounting guide. After you understand the basics, read this guide to learn how to create feedback loops where Python updates the component programmatically.
Note
This guide explains how to recreate behavior that's similar to native Streamlit widgets: setting a component's state from Session State. In general, you don't need to pass a component's state back into itself. This is just one pattern to manage a component's state through Session State.
However, because components can have multiple states and triggers, you must work with st.session_state.<key>.<state> for custom components instead of simply st.session_state.<key> like you do with native Streamlit widgets.
Prerequisites
Before you read this guide, you should understand the following concepts:
The communication cycle
Custom components communicate through a cycle:
- Python → JavaScript: Python sends data to your component via the
dataparameter. - User interaction: The user interacts with your component in the browser.
- JavaScript → Python: Your component sends the result back via
setStateValue()orsetTriggerValue(). - Python updates: All related callback functions are executed and the component's result is updated in a script rerun.
For native Streamlit widgets, you can assign a key and set a widget's state through Session State. However, custom components don't automatically pass information from Session State to their associated component. To programmatically update a component from Python, you need to pass new data to the component's data parameter. If you want to set your component's state from Session State, you must pass the component's Session State values to the component's data parameter.
Creating a feedback loop
Here's a text input component that demonstrates this pattern. This is the text input component shown in the quickstart guide.
The JavaScript side
Your component's JavaScript function must read data to initialize and update its state:
The conditional expression (if (input.value !== data.value)) updates the input field when Python sends new data. Because the Python code (in the next section) sets the state value using the default parameter, the component doesn't need to use setStateValue() here.
The Python side
In Python, you can create a wrapper function that reads from Session State and passes updated data:
To create a clean mounting command, the wrapper lets you declare the component's label, initial value, key, and callback function. Within the
wrapper, when a key is provided, use the get() method on st.session_state to read the current component state. This prevents an error on the first script run, before the component is mounted.
The get() method is used twice. First, get the component state from its key. Component states are dictionaries of state and trigger values. In this case, the component has a single state named "value". Then, from the component state, get the value of the the "value" state. If the "value" state isn't defined or no key is provided, use the provided default value.
Finally, within the wrapper, call the raw mounting command, passing in the current data. You can directly pass through the key and on_change values. However, data and default are constructed from the previous logic that uses the existing component state.
Programmatic updates
With this pattern, you can update the component from Python by modifying Session State:
When you click a button, it modifies Session State. On the rerun, the wrapper reads the new value from Session State and passes it to the component via data. The JavaScript sees the updated data.value and updates the input field.
Complete example
See the Text input component example for the full working code.
Key takeaways
datais one-way: Python sends data to JavaScript, but changes in JavaScript don't automatically update Python.- State values bridge the gap: Use
setStateValue()to send user interactions back to Python. - Session State enables control: Store component state with a
key, then read from Session State to updatedata. - Sync carefully: Check if the value has changed before updating DOM elements to avoid interfering with user input.
Still have questions?
Our forums are full of helpful information and Streamlit experts.
