✨ Streamlit Elements
Create a draggable and resizable dashboard in Streamlit, featuring Material UI widgets, Monaco editor (Visual Studio Code), Nivo charts, and more!
Demo
Getting started
1. Introduction
Streamlit Elements is a component that gives you the tools to compose beautiful applications with Material UI widgets, Monaco, Nivo charts, and more.
It also includes a feature to create draggable and resizable dashboards.
Installation
pip install streamlit-elements==0.1
Caution: It is recommended to pin the version to 0.1. Future versions might introduce breaking API changes.
Available elements and objects
Here is a list of elements and objects you can import in your app:
Element | Description |
---|
elements | Create a frame where elements will be displayed. |
dashboard | Build a draggable and resizable dashboard. |
mui | Material UI (MUI) widgets and icons. |
html | HTML objects. |
editor | Monaco code and diff editor that powers Visual Studio Code. |
nivo | Nivo chart library. |
media | Media player. |
sync | Callback to synchronize Streamlit's session state with elements events data. |
lazy | Defer a callback call until another non-lazy callback is called. |
Caution
- A few Material UI widgets may not work as expected (ie. modal dialogs and snackbars).
- Using many element frames can significantly impact your app's performance. Try to gather elements in few frames at most.
2. Display elements
2.1. Create an element with a child
from streamlit_elements import elements, mui, html
with elements("new_element"):
mui.Typography("Hello world")
2.2. Create an element with multiple children
with elements("multiple_children"):
mui.Button(
mui.icon.EmojiPeople,
mui.icon.DoubleArrow,
"Button with multiple children"
)
with mui.Button:
mui.icon.EmojiPeople()
mui.icon.DoubleArrow()
mui.Typography("Button with multiple children")
2.3. Create an element with nested children
with elements("nested_children"):
with mui.Paper:
with mui.Typography:
html.p("Hello world")
html.p("Goodbye world")
2.4. Add properties to an element
with elements("properties"):
with mui.Paper(elevation=3, variant="outlined", square=True):
mui.TextField(
label="My text input",
defaultValue="Type here",
variant="outlined",
)
mui.Collapse(in_=True)
2.5. Apply custom CSS styles
2.5.1. Material UI elements
with elements("style_mui_sx"):
mui.Box(
"Some text in a styled box",
sx={
"bgcolor": "background.paper",
"boxShadow": 1,
"borderRadius": 2,
"p": 2,
"minWidth": 300,
}
)
2.5.2. Other elements
with elements("style_elements_css"):
html.div(
"This has a hotpink background",
css={
"backgroundColor": "hotpink",
"&:hover": {
"color": "lightgreen"
}
}
)
3. Callbacks
3.1. Retrieve an element's data
import streamlit as st
with elements("callbacks_retrieve_data"):
if "my_text" not in st.session_state:
st.session_state.my_text = ""
def handle_change(event):
st.session_state.my_text = event.target.value
mui.Typography(st.session_state.my_text)
mui.TextField(label="Input some text here", onChange=handle_change)
3.2. Synchronize a session state item with an element event using sync()
with elements("callbacks_sync"):
from streamlit_elements import sync
if "my_event" not in st.session_state:
st.session_state.my_event = None
if st.session_state.my_event is not None:
text = st.session_state.my_event.target.value
else:
text = ""
mui.Typography(text)
mui.TextField(label="Input some text here", onChange=sync("my_event"))
3.3. Avoid too many reloads with lazy()
with elements("callbacks_lazy"):
from streamlit_elements import lazy
if "first_name" not in st.session_state:
st.session_state.first_name = None
st.session_state.last_name = None
if st.session_state.first_name is not None:
first_name = st.session_state.first_name.target.value
else:
first_name = "John"
if st.session_state.last_name is not None:
last_name = st.session_state.last_name.target.value
else:
last_name = "Doe"
def set_last_name(event):
st.session_state.last_name = event
mui.Typography("Your first name: ", first_name)
mui.Typography("Your last name: ", last_name)
mui.TextField(label="First name", onChange=lazy(sync("first_name")))
mui.TextField(label="Last name", onChange=lazy(set_last_name))
mui.Button("Update first namd and last name", onClick=sync())
3.4. Invoke a callback when a sequence is pressed using event.on_hotkey()
with elements("callbacks_hotkey"):
from streamlit_elements import event
def hotkey_pressed():
print("Hotkey pressed")
event.Hotkey("g", hotkey_pressed)
event.Hotkey("h", hotkey_pressed, bindInputs=True)
mui.TextField(label="Try pressing 'h' while typing some text here.")
event.Hotkey("ctrl+f", hotkey_pressed, overrideDefault=True)
3.5. Invoke a callback periodically using event.on_interval()
with elements("callbacks_interval"):
def call_every_second():
print("Hello world")
event.Interval(1, call_every_second)
4. Draggable and resizable dashboard
with elements("dashboard"):
from streamlit_elements import dashboard
layout = [
dashboard.Item("first_item", 0, 0, 2, 2),
dashboard.Item("second_item", 2, 0, 2, 2, isDraggable=False, moved=False),
dashboard.Item("third_item", 0, 2, 1, 1, isResizable=False),
]
with dashboard.Grid(layout):
mui.Paper("First item", key="first_item")
mui.Paper("Second item (cannot drag)", key="second_item")
mui.Paper("Third item (cannot resize)", key="third_item")
def handle_layout_change(updated_layout):
print(updated_layout)
with dashboard.Grid(layout, onLayoutChange=handle_layout_change):
mui.Paper("First item", key="first_item")
mui.Paper("Second item (cannot drag)", key="second_item")
mui.Paper("Third item (cannot resize)", key="third_item")
5. Other third-party elements
5.1. Monaco code and diff editor
with elements("monaco_editors"):
from streamlit_elements import editor
if "content" not in st.session_state:
st.session_state.content = "Default value"
mui.Typography("Content: ", st.session_state.content)
def update_content(value):
st.session_state.content = value
editor.Monaco(
height=300,
defaultValue=st.session_state.content,
onChange=lazy(update_content)
)
mui.Button("Update content", onClick=sync())
editor.MonacoDiff(
original="Happy Streamlit-ing!",
modified="Happy Streamlit-in' with Elements!",
height=300,
)
5.2. Nivo charts
with elements("nivo_charts"):
from streamlit_elements import nivo
DATA = [
{ "taste": "fruity", "chardonay": 93, "carmenere": 61, "syrah": 114 },
{ "taste": "bitter", "chardonay": 91, "carmenere": 37, "syrah": 72 },
{ "taste": "heavy", "chardonay": 56, "carmenere": 95, "syrah": 99 },
{ "taste": "strong", "chardonay": 64, "carmenere": 90, "syrah": 30 },
{ "taste": "sunny", "chardonay": 119, "carmenere": 94, "syrah": 103 },
]
with mui.Box(sx={"height": 500}):
nivo.Radar(
data=DATA,
keys=[ "chardonay", "carmenere", "syrah" ],
indexBy="taste",
valueFormat=">-.2f",
margin={ "top": 70, "right": 80, "bottom": 40, "left": 80 },
borderColor={ "from": "color" },
gridLabelOffset=36,
dotSize=10,
dotColor={ "theme": "background" },
dotBorderWidth=2,
motionConfig="wobbly",
legends=[
{
"anchor": "top-left",
"direction": "column",
"translateX": -50,
"translateY": -40,
"itemWidth": 80,
"itemHeight": 20,
"itemTextColor": "#999",
"symbolSize": 12,
"symbolShape": "circle",
"effects": [
{
"on": "hover",
"style": {
"itemTextColor": "#000"
}
}
]
}
],
theme={
"background": "#FFFFFF",
"textColor": "#31333F",
"tooltip": {
"container": {
"background": "#FFFFFF",
"color": "#31333F",
}
}
}
)
5.3. Media player
with elements("media_player"):
from streamlit_elements import media
media.Player(url="https://www.youtube.com/watch?v=iik25wqIuFo", controls=True)