Did you know that most Streamlit widgets are based on React Base Web?
Given this, let’s start by installing that dependency into your frontend code. Remember that every npm
command should be run in the frontend
folder which contains package.json
.
$ cd streamlit_custom_slider/frontend
$ npm add baseui styletron-engine-atomic styletron-react
$ npm add @types/styletron-standard @types/styletron-react @types/styletron-engine-atomic
This will install baseui
and styletron
in your project. The add
command will add the dependency into package.json
— this ensures that other people will have it installed when running npm install
(after cloning the project).
In order to make the TypeScript linter happy, the above code will also add the type definition for the newly-installed libraries.
Now that you have baseui
, be sure to check out the slider docs, have a look at the code sample, and play around with the props. When you are ready to continue, add the basic Slider example into your CustomSlider.tsx
.
import React, { useEffect, useState } from "react";
import { Streamlit, withStreamlitConnection } from "./streamlit";
import { Slider } from "baseui/slider";
const CustomSlider = () => {
// Define an internal state for your component, called "value" with an initial value of [10]
// setValue is used to modify the state with a new Array of numbers
const [value, setValue] = useState([10]);
useEffect(() => Streamlit.setFrameHeight());
// Render this React component as a stateless baseui Slider
return (
<>
<Slider
value={value}
onChange={({ value }) => value && setValue(value)}
onFinalChange={({ value }) => console.log(value)}
/>
</>
);
};
export default withStreamlitConnection(CustomSlider);
Base Web uses Styletron to style its components, but this is not within the scope of this tutorial — so, for now, we will copy-paste the code from the README to use Styletron’s default LightTheme. Just paste the snippet in the index.tsx
file:
import React from "react";
import ReactDOM from "react-dom";
import CustomSlider from "./CustomSlider";
// Lots of import to define a Styletron engine and load the light theme of baseui
import { Client as Styletron } from "styletron-engine-atomic";
import { Provider as StyletronProvider } from "styletron-react";
import { ThemeProvider, LightTheme } from "baseui";
const engine = new Styletron();
// Wrap your CustomSlider with the baseui light theme
ReactDOM.render(
<React.StrictMode>
<StyletronProvider value={engine}>
<ThemeProvider theme={LightTheme}>
<CustomSlider />
</ThemeProvider>
</StyletronProvider>
</React.StrictMode>,
document.getElementById("root")
);
Since we compiled new dependencies, you may need to rerun npm run start
.
By the way, just in case you did not follow the beginning of the tutorial, here is the __init__.py
— this is used to declare the connection to the frontend component:
import streamlit.components.v1 as components
# Create a function _component_func which will call the frontend component when run
_component_func = components.declare_component(
"custom_slider",
url="http://localhost:3001", # Fetch frontend component from local webserver
)
# Define a public function for the package,
# which wraps the caller to the frontend code
def st_custom_slider():
component_value = _component_func()
return component_value
Though we won’t deal with retrieving a value from the CustomSlider
in this part of the tutorial, be sure to refactor your app.py
to store the return value from st_custom_slider
anyway:
import streamlit as st
from streamlit_custom_slider import st_custom_slider
st.title("Testing Streamlit custom components")
# Store and display the return value of your custom component
v = st_custom_slider()
st.write(v)
Go back to your browser by opening http://localhost:8501
to see your slider in action.
We should pause here for a moment to understand the code inside CustomSlider.tsx
const [value, setValue] = useState([10]);
uses the useState React hook to initialize a local state in your functional component without requiring a rewrite of your component using the class-based structure. This state is preserved if the component gets rerendered by the page. We are using this state variable to store the value of the slider to display because the Base Web Slider doesn’t save it.At this stage, you have manipulated the two most important React hooks, useEffect and useState. That is as far as we will go on developing your React expertise for Streamlit! There’s still a lot to learn to optimize your component, but that can wait.
Slider
— a React component from baseui
— to encapsulate HTML/CSS/JS code to render a slider. We customized it by passing it props like value
or onChange
.<Slider value={value}...
does not truly look like HTML. The value={value}
part feels like f-string interpolation in Python. The reason is that values returned by React components for browser rendiering use a special syntax called JSX, a JavaScript extension which adds HTML-like syntax and produces React elements.value={value}
. In short, you are returning a Slider
with the property value
, set with the content of the value
variable stored as the component’s state.onChange
event is called every time the slider value is changed. It’s important to update the inner state of the component at each movement, because the rendered slider displays the internal state. If you omit this line, the slider will be immovable.onChange
event, we pass an anonymous function as callback to the event, so it runs every time you interact with the slider. If you omit the () =>
it would fire every time the component re-renders.onFinalChange
is called when the mouse releases the slider. That’s when we will want to return the slider value to the Python script. For now, let’s just log the value of the slider to the console. You can check the console logs in your browser’s devtools (normally by pressing F12
then going to your console log). Think of it as your print
method from Python!Great job! The slider is moving, and the value of the slider is printed in the JS console on mouse release.