Streamlit bridges Python and React

This section discusses how Streamlit connects the worlds of Python and React. The concepts here should prove useful when building components but, if you’re only interested in the hands-on tutorial, feel free to jump right into the Project Setup!

Each Streamlit call on the Python side loads up a React component from the running Streamlit server, which is then rendered onto your web browser. If we go under the hood of a st.markdown call:

Python React bridge

The Streamlit call st.markdown is mapped to a Markdown React component. This Markdown component is a Javascript class or function that is rendered as a bunch of HTML/CSS/JS code. The component is then implemented inside a Markdown.tsx file and accessed from a running browser via a path to the filesystem. For this example, it defines a functional component which returns a single <h1> block for React to render in the browser.

You may have heard about React as one of the preferred JavaScript libraries for building interactive frontend interfaces, largely because it uses a declarative, component-based approach to encapsulate and render frontend logic. Don’t worry if you are not familiar with React yet — we will go through the basics during the following exercises!
One of the main benefits of Streamlit is that it enables you to assign each call to an independent React component with its own state and logic, all rendered in your web browser. We could actually say Streamlit is a bridge between React and Python.

Each rendered React component instance is independent and tied to a Streamlit call with its own encapsulated logic and internal state. For example, a st.slider call is mapped to a Slider component and any Python arguments are passed as properties — conveniently named “props” — to the component for a custom render.

Slider

When you interact with a given widget, a user-implemented JavaScript callback can set the internal state of the widget, then push the value back to the Python side. Such a push triggers a rerun of the Python script, with the new value set up in the variable returned by the st.slider call.

On script reruns, the current values of all widgets are gathered from the React side and propagated into the corresponding Python variables.

Interactive Slider


Streamlit components follow the same model:

  1. A Streamlit Python call is mapped into a set of HTML/CSS/JS code or frontend code. Python arguments passed through the Streamlit call are then sent through the body of a JavaScript event. In the case of the React template, those arguments then become props of the React component.
  2. The frontend component gets rendered inside an IFrame on the browser.
  3. If the user interacts with the widget, then the component handles a callback which may modify the internal state of the frontend component — this will then send the value back to the Streamlit Python script through the body of a JavaScript event. This also triggers a rerun of the Python script where the Python variable returned by the Streamlit call gets assigned the new value.

Workflow

So the following questions arise for a component creator :

  1. How do we tell Streamlit to map a Streamlit call to specific frontend code?
  2. How do we define what is rendered on the browser?
  3. How do we handle interactivity and send data back to the Python script?

For this tutorial, we will use React to build a CustomSlider component:

  1. This slider will be called on the Python side with a st_custom_slider call and will retrieve the frontend code from the CustomSlider React component.
  2. It will render an interactive slider using an external library called Base Web.
  3. Interacting with the slider triggers a Streamlit rerun only when the mouse button is released.