Skip to main content

Looking for a fully open-source alternative?

Check out st-rsuite — and try the sample app!


WARNING — MUI X Pro Licensing

MUI was contacted and asked to provide a development/demo license solely to run the showcase app without displaying a watermark. They refused.

I therefore strongly encourage everyone NOT to purchase a MUI X Pro license. st-mui no longer ships MUI X Pro: both range widgets are implemented with MIT-licensed Community pickers and require no license key.


st-mui

Material UI and MUI X components for Streamlit, built with Components v2

PyPI version Downloads Python ≥3.10 License
Open in Streamlit


Components

Component Description License Streamlit equivalent
time_picker Clock UI, AM/PM toggle, min/max bounds MIT st.time_input
date_time_picker Combined date + time, AM/PM toggle, calendar popover MIT st.date_input + st.time_input
date_picker Calendar popover with format control MIT st.date_input
date_range_picker Validated start/end date selection MIT st.date_input (range mode)
date_time_range_picker Validated start/end datetime selection MIT --
tree_view Hierarchical tree with checkboxes and multi-select MIT --
autocomplete Searchable single/multi-select and free-form entry MIT st.selectbox / st.multiselect
slider Numeric single-value and range slider with marks MIT st.slider
rating Accessible star rating with fractional precision MIT --
data_grid Sortable, filterable, pageable Community Data Grid MIT st.dataframe

Installation

uv add st-mui

or with pip:

pip install st-mui

Quick start

import streamlit as st
from datetime import time, datetime, date, timedelta
from st_mui import (
    time_picker, date_time_picker, date_picker,
    date_range_picker, date_time_range_picker,
    tree_view, autocomplete, slider, rating, data_grid,
)

t = time_picker(label="Pick a time", value=time(9, 30), ampm=True, key="my_time")

# A default recomputed on every rerun replaces the user's in-progress
# selection, so anchor dynamic values in session state.
if "start" not in st.session_state:
    st.session_state["start"] = datetime.now().replace(second=0, microsecond=0)
start_value = st.session_state["start"]

dt = date_time_picker(label="Select date & time", value=start_value, key="my_datetime")

d = date_picker(label="Pick a date", value=date.today(), key="my_date")

start, end = date_range_picker(
    label="Trip dates",
    value=(date.today(), date.today() + timedelta(days=7)),
    key="my_range",
)

start_dt, end_dt = date_time_range_picker(
    label="Event",
    value=(start_value, start_value + timedelta(hours=2)),
    key="my_dt_range",
)

selected = tree_view(
    items=[
        {"id": "docs", "label": "Documents", "children": [
            {"id": "resume", "label": "Resume.pdf"},
        ]},
        {"id": "photos", "label": "Photos"},
    ],
    checkbox_selection=True,
    multi_select=True,
    key="my_tree",
)

destination = autocomplete(
    [{"label": "Los Angeles", "value": "LAX"}, "Other"],
    label="Destination",
    key="destination",
)

price_range = slider(
    "Price range", value=(20, 80), min_value=0, max_value=100, key="price"
)

score = rating("Score", value=4.5, precision=0.5, key="score")

grid_state = data_grid(
    rows=[{"id": 1, "name": "Ada"}, {"id": 2, "name": "Grace"}],
    columns=["name"],
    checkbox_selection=True,
    key="people",
)

Behavior notes

  • Date-time pickers represent browser-local wall-clock values and return timezone-naive Python datetime objects. This avoids silently shifting a selected time to UTC and works consistently on every supported Python version, including Python 3.10. Any timezone information on input values is intentionally ignored.
  • The range picker on_change callback runs once when either the start or end value changes.
  • minutes_step constrains which minutes the picker accepts, so a value whose minute is not a multiple of the step renders in a validation error state. Round dynamic defaults such as datetime.now() up to the next boundary, and hold them in st.session_state so a rerun does not replace the user's in-progress selection.
  • All five date/time widgets support helper text, clearability, read-only mode, past/future guards, configurable initial/available views, and keyboard input. Date widgets can show ISO week numbers; time widgets support minute-step and display-format controls.
  • Range widgets are composed from two MIT Community fields and enforce start ≤ end in both the browser and Python. Historical license_key and calendars arguments remain accepted as compatibility shims but are no longer used.
  • tree_view(disabled=True) disables selection and expansion for every item.
  • autocomplete values are JSON-safe strings, numbers, or booleans. Dictionary options let display labels differ from returned values. Integer values must fit JavaScript's exact integer range.
  • A two-item slider value enables range mode. Slider state is committed when the drag ends rather than on every pixel moved. With step=None, provide a non-empty marks list and use marked values for the initial selection. marks=True is capped at 1,000 generated marks; use explicit marks for larger numeric ranges.
  • rating precision must be from 0.01 through 1, divide one star into an integer number of steps, and align with the selected value.
  • data_grid uses the MIT Community package only. Its single on_change callback covers selection, sorting, filtering, and pagination. Community pagination is limited to 100 rows per page.

API

time_picker

time_picker(
    label="Select a time",
    value=None,           # time object or HH:MM string
    ampm=True,            # 12-hour vs 24-hour
    min_time=None,
    max_time=None,
    disabled=False,
    on_change=None,
    key=None,
    *,
    helper_text=None,
    clearable=True,
    read_only=False,
    disable_past=False,
    disable_future=False,
    open_to=None,        # "hours", "minutes", or "seconds"
    views=None,
    minutes_step=1,
    format=None,
) -> time | None

date_time_picker

date_time_picker(
    label="Select date & time",
    value=None,           # datetime object or ISO string
    min_datetime=None,
    max_datetime=None,
    ampm=True,
    disabled=False,
    on_change=None,
    key=None,
    *,
    helper_text=None,
    clearable=True,
    read_only=False,
    disable_past=False,
    disable_future=False,
    open_to=None,
    views=None,          # year/month/day/hours/minutes/seconds
    minutes_step=1,
    format=None,
) -> datetime | None

date_picker

date_picker(
    label="Select a date",
    value=None,           # date object or YYYY-MM-DD string
    min_date=None,
    max_date=None,
    format="MM/DD/YYYY",  # MUI format tokens
    disabled=False,
    on_change=None,
    key=None,
    *,
    helper_text=None,
    clearable=True,
    read_only=False,
    disable_past=False,
    disable_future=False,
    open_to=None,        # "year", "month", or "day"
    views=None,
    display_week_number=False,
) -> date | None

date_range_picker

date_range_picker(
    label="Select date range",
    value=None,           # tuple of (date, date) or (str, str)
    min_date=None,
    max_date=None,
    calendars=2,          # deprecated compatibility argument
    disabled=False,
    license_key=None,     # deprecated compatibility argument
    on_change=None,
    key=None,
    *,
    start_label=None,
    end_label=None,
    format="MM/DD/YYYY",
    helper_text=None,
    clearable=False,
    read_only=False,
    disable_past=False,
    disable_future=False,
    open_to=None,
    views=None,
    display_week_number=False,
) -> tuple[date | None, date | None]

date_time_range_picker

date_time_range_picker(
    label="Select date & time range",
    value=None,           # tuple of (datetime, datetime) or (str, str)
    min_datetime=None,
    max_datetime=None,
    ampm=True,
    disabled=False,
    license_key=None,     # deprecated compatibility argument
    on_change=None,
    key=None,
    *,
    start_label=None,
    end_label=None,
    format=None,
    helper_text=None,
    clearable=False,
    read_only=False,
    disable_past=False,
    disable_future=False,
    open_to=None,
    views=None,
    minutes_step=1,
) -> tuple[datetime | None, datetime | None]

tree_view

tree_view(
    items=None,           # list of {"id", "label", "children": [...]}
    label=None,
    multi_select=False,
    checkbox_selection=True,
    default_expanded=None,
    default_selected=None,
    disabled=False,
    on_change=None,
    key=None,
) -> list[str]  # selected item IDs

autocomplete

autocomplete(
    options=None,        # scalars or {"label", "value", "disabled"} mappings
    label="Select an option",
    value=None,
    multiple=False,
    free_solo=False,
    placeholder=None,
    helper_text=None,
    clearable=True,
    disabled=False,
    on_change=None,
    key=None,
) -> str | int | float | bool | list | None

slider

slider(
    label="Select a value",
    value=None,          # number or two-number sequence for range mode
    min_value=0,
    max_value=100,
    step=1,             # None enables marks-only selection
    marks=False,        # bool or [{"value": 0, "label": "Low"}, ...]
    value_label_display="auto",
    disabled=False,
    on_change=None,
    key=None,
) -> int | float | tuple[int | float, int | float]

rating

rating(
    label="Rating",
    value=None,
    max_value=5,         # integer from 1 through 100
    precision=1.0,       # 1/n from 0.01 through 1; value must align
    size="medium",      # "small", "medium", or "large"
    disabled=False,
    read_only=False,
    clearable=True,
    on_change=None,
    key=None,
) -> float | None

data_grid (Community)

data_grid(
    rows=None,           # records or a DataFrame containing the id field
    columns=None,        # field names or supported column mappings
    id_field="id",
    selected_rows=None,
    sort_model=None,
    filter_model=None,
    page_size=10,        # Community edition maximum: 100
    page_size_options=(10, 25, 50, 100),
    height=400,
    checkbox_selection=False,
    density="standard",
    disabled=False,
    on_change=None,
    key=None,
) -> dict  # selection, sort, filter, and pagination models

The Community Data Grid supports JSON-safe rows, unique string/numeric row IDs, column types string, number, boolean, and singleSelect, plus Pythonic column aliases such as header_name, min_width, and value_options.

Range picker migration

The range widgets use paired MIT-licensed Community fields. Applications upgrading from 0.4.0 can keep passing license_key or calendars while they remove those arguments; both are ignored and license_key emits a DeprecationWarning. No MUI X license environment variable is read or bundled.

Running the example

pip install st-mui
streamlit run examples/showcase.py

Development

# Clone and install
git clone https://github.com/lperezmo/st-mui.git
cd st-mui
uv sync --dev

# Build frontend
cd st_mui/frontend
npm install
npm test
npm run typecheck
npm run build
cd ../..

# Run Python tests
uv run --with pytest pytest

# Run showcase
uv run streamlit run examples/showcase.py

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

st_mui-0.5.2.tar.gz (1.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

st_mui-0.5.2-py3-none-any.whl (1.6 MB view details)

Uploaded Python 3

File details

Details for the file st_mui-0.5.2.tar.gz.

File metadata

  • Download URL: st_mui-0.5.2.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for st_mui-0.5.2.tar.gz
Algorithm Hash digest
SHA256 68de518f58a55ac66b7232b8084d729cda2494d61f2fcf15f4ea051e2bf607d5
MD5 b7440a155a44871cb6c5074a88fe5e52
BLAKE2b-256 0fe49ad1a53b9340db971f3b9f2fc11090b3a81009873bc516ff1ae76f3e3e2d

See more details on using hashes here.

File details

Details for the file st_mui-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: st_mui-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for st_mui-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7c39301675c0e5ae84da7e8bf8c706cc1e474abd8c4efaafb01d6a9370dd78b9
MD5 7e2456fc7ecaaa453a514e249403875c
BLAKE2b-256 b744439d8a11cf2416ee738d40237b569288bf66c7cbe9f45781896a33300764

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.5

2 files

0.5.4

2 files

0.5.3

2 files

This release

0.5.2 This release

2 files

0.5.1

2 files

0.4.0

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.2.5

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page