Render Component from Streamlit

In the previous section, we built a <CustomSlider /> instance and removed the connection to Streamlit so it could be rendered — the component is still being served on http://localhost:3001. Let’s rebuild the component connection to to Streamlit so we can call it from our app.py file.

Python Call to the Frontend Dev Web Server

First, let’s write the Python code that calls the frontend component.

Define a st_custom_slider as a public function of our package by implementing it inside the streamlit_custom_slider/__init__.py file. It will act as a wrapper to the function that calls the frontend web server.

# Define a public function for the package,
# which will wrap the caller to the frontend code
def st_custom_slider():
    return None

The st_custom_slider should now be available in your streamlit-custom-slider package. As you have already installed the package locally in editable mode with pip install -e ., your IDE should suggest it to you.

The app.py file at the root of your project should still be running from the previous section. You can call your component from there :

# Import the wrapper function from your package
from streamlit_custom_slider import st_custom_slider
import streamlit as st

st.title("Testing Streamlit custom components")
st_custom_slider()

How do we tell Streamlit to map a Streamlit call to specific frontend code ?

When a user calls st_custom_slider in the Python script, we want Streamlit to access http://localhost:3001 for the index.html which contains CustomSlider.tsx .

The declare_component method—from streamlit.components.v1—is a Streamlit method that defines the location for the component’s frontend code (in this example, through the URL http://localhost:3001). This method returns a callable function that we can then use to access the component resource as defined in the declaration.

Access frontend definition

Add the declare_component call inside your st_custom_slider wrapper in streamlit_custom_slider/__init__.py.

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

Finally, run your app with streamlit run app.py like normal and open a new tab in your browser on http://localhost:8501:

First try

While the title appears, the React component with a Hello world block from st_custom_slider does not. This is expected because Streamlit renders components which respond to a specific JS event — and we have removed all event listeners from our React component. So now it’s time to bring the Streamlit JS event listeners back!

Streamlit Connection on React

If you look back at the code in the template, there’s a specific withStreamlitConnection that bootstraps a React component with all Streamlit Javascript events. So let’s try to add it back in frontend/src/CustomSlider.tsx.

import React from "react";
import {
  withStreamlitConnection
} from "streamlit-component-lib";

const CustomSlider = () => {
  return <h1>Hello world</h1>;
};

// Link the component to Streamlit JS events.
export default withStreamlitConnection(CustomSlider);

The line export default withStreamlitConnection(CustomSlider) exposes the CustomSlider publicly in the project so that it can be imported into index.tsx. The withStreamlitConnection call is a React higher-order class that sets up all Streamlit event listeners for the wrapped component. Check it out in src/streamlit/StreamlitReact.tsx.

On http://localhost:3001, the component does not render anymore because it expects a Streamlit JS event, but if you go to http://localhost:8501 you should see that…there is still no sign of the component.

Now is a good time to talk about the browser developer tool. Go ahead and open the Inspector in your browser devtools by right-clicking on the page and selecting Inspect element, or open settings and browse to it (there are also keyboard shortcuts: Command + Option + i on Mac or Ctrl + Shift + i on Windows/Unix).

If you look for your element, you may actually find the <h1>Hello world</h1> block, but the iFrame that contains it has a height of 0.

devtools

Configure the Height of the iFrame Containing the Component

Recall that the custom component is mounted inside an iFrame. We need to use the Streamlit.setFrameHeight function to define the height of the iFrame so that it is large enough to display the contained component. By default, the height is set to the scrollable height of the rendered component; consequently, we need to wait for the component to render before configuring the height.

For that we have the specific useEffect React hook, a function which runs a callback after the component has been rendered on the browser. We want to call Streamlit.setFrameHeight to setup the iFrame height in the side effect. You should try to update the height of the frame every time the size of the component changes.

import React, { useEffect } from "react";
import { Streamlit, withStreamlitConnection } from "./streamlit";

const CustomSlider = () => {
  // useEffect is a specific React hook which calls its input after the component has been rendered on the browser.
  // The callback function properly sets the height of the HTML block wrapping the component. By default it checks the scrollable height of the returned block after rendering and sets it as the iframe height.
  useEffect(() => Streamlit.setFrameHeight());

  return <h1>Hello world</h1>;
};

export default withStreamlitConnection(CustomSlider);

If all went well, npm run start is still running and delivering your custom component on http://localhost:3001 while streamlit run app.py is on http://localhost:8501.

Now your Streamlit app fetches the custom component from the Webpack server and renders it on your web browser through a specific JS event. The iFrame container then updates its height to the size of the rendered component — have a look at http://localhost:8501 to see!

Final render

Great work, you are finally seeing the contents of CustomSlider.tsx in action now that it is accessed from a Streamlit script.

Going back to the CustomSlider.tsx, you can add more text in your component and have it render immediately since the components web server offers live reloading:

import React, { useEffect } from "react";
import { Streamlit, withStreamlitConnection } from "./streamlit";

const CustomSlider = () => {
  useEffect(() => Streamlit.setFrameHeight());

  return (
    <>
      <h1>Hello Streamlit custom component</h1>
      <p>Nice to meet you</p>
    </>
  );
};

export default withStreamlitConnection(CustomSlider);

You’re also free to edit your app.py which should also update in realtime!

Remember that whenever you change the source code—whether it’s in Python app.py or JavaScript CustomSlider.tsx—the feedback in your browser is immediate. If you’ve come this far, congratulations! You are ready to edit your React component.