Render the baseui Slider

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.

First slider

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.

  • We import and use 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.
  • You may have noticed that the output of our function <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.
  • In JSX, you can evaluate a variable by wrapping it in brackets, hence the 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.
  • The 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.
  • To define the 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!

Console log

Great job! The slider is moving, and the value of the slider is printed in the JS console on mouse release.