Skip to main content

StreamTree: declarative, typed composition for Streamlit.

Project description

StreamTree

PyPI version Python versions License CI Ruff

Declarative, typed composition for Streamlit. StreamTree adds components, layout primitives, scoped session state, and test-friendly tree rendering while keeping Streamlit’s execution model and widgets. No JavaScript or separate frontend build is required.

Overview

Traditional scripts With StreamTree
Layout and widgets mixed in one long file @component functions return an element tree
Many ad hoc st.session_state keys state() and helpers scoped to the render path
Structure is hard to inspect or snapshot streamtree.testing.render_to_tree() and 0.10.0+ summarize_tree_kinds() for tests and docs

StreamTree is an architecture layer for Streamlit, not a React-style web framework.

Capabilities

  • Components and elements@component, render / render_app, layouts (Page, Card, Grid, VStack, Form, Tabs, Sidebar, Routes, ErrorBoundary, Dialog, Popover, and more), and widget wrappers.
  • App shell (0.3+)App with a guarded st.set_page_config, plus optional sidebar and main composition via render_app.
  • Theming (0.3+)Theme, ThemeRoot, theme(), theme_css(), and app_context.provider(theme=...).
  • Background work (0.3+)streamtree.asyncio.submit / submit_many and TaskHandle for stdlib-thread jobs you poll across reruns; set_task_progress / TaskHandle.progress() (0.5+); 0.7+ cooperative cancel via TaskHandle.cancel(), is_task_cancel_requested, and complete_cancelled; 0.8.0+ dismiss_task clears terminal slots before reusing a key; 0.9.0+ dismiss_tasks for batch clears (see module docstring in streamtree.asyncio).
  • Multipage helpers (0.5+)streamtree.helpers.pages (discover_pages, page_links, group_page_entries_by_order_prefix, page_links_sidebar_sections, multipage_sidebar_nav) for Streamlit’s pages/ layout; ships in the default install (the [pages] extra remains reserved for future pinned deps). iter_page_entries / prefetch_page_sources (0.9.0+) optionally compile() page sources for early syntax checks without importing modules. streamtree init --with-pages (0.8.0+) scaffolds a sidebar wired to discover_pages + page_links.
  • Forms (0.3+) — Pydantic-oriented helpers: bind_str_fields / str_text_inputs, bind_numeric_fields / number_inputs (0.4+), and bind_bool_fields / bool_field_names (0.9.0+) for int / float / bool fields (optional unions use model defaults where applicable).
  • CLI (0.4+) — Optional streamtree[cli]: streamtree run delegates to Streamlit; streamtree doctor prints versions; streamtree init (0.6+) scaffolds app.py and optional pages/; 0.10.0+ --template / -t chooses default, crud, explore, or enterprise shells (see examples/streamtree_run_demo.md).
  • Statestate, toggle_state, form_state, memo, cache, 0.10.0+ memo_subtree (path-scoped memo for heavy subtrees).
  • Routing and context — Query-param routing (streamtree.routing, including 0.9.0+ clear_query_param, clear_route, update_query_params), ErrorBoundary, streamtree.forms utilities, and app_context.provider / lookup for shared values. Native pages/ scripts are separate entrypoints; Routes switches subtrees inside one script via query params—pick one primary navigation model per app (see docs/ROADMAP.md).
  • Optional auth (0.6+)pip install "streamtree[auth]": AuthGate + streamtree.auth.build_authenticator for streamlit-authenticator (see examples/auth_demo.py; treat config as trusted secrets). Alternative identity providers stay app-specific unless/until a pinned abstraction ships; see docs/DEPENDENCY_STRATEGY.md (Auth extra).
  • Optional UI extras (0.6+)pip install "streamtree[ui]": ColoredHeader, VerticalSpaceLines, SocialBadge, StyleMetricCards, BottomDock, FloatingActionButton, Stoggle, TaggerRow, MentionChip (curated streamlit-extras wrappers).
  • Overlays (0.6+)Dialog / Popover elements mapped to st.dialog / st.popover. On older Streamlit builds without st.dialog, Dialog shows a warning and renders its children inline on the page (not a modal); Popover falls back to st.expander.
  • Portals and split shell (0.9.0+)Portal / PortalMount (named slots; see docs/PHASE2_PORTALS_AND_PREFETCH.md), SplitView (narrow + main columns as a pseudo-sidebar), and streamtree.portals helpers for gather/render wiring.
  • Form layout (0.9.0+)streamtree.forms_layout.model_field_grid and build_model_from_bindings for Pydantic models in row/column grids, including bool fields (see docs/PHASE2_FORMS.md, examples/phase2_layout_demo.py).
  • Data toolkit (0.8+)[tables] / DataGrid, [charts] / Chart, AltairChart, EChartsChart; streamtree.loading.match_task and 0.10.0+ match_task_many / submit_many_ordered; routing.sync_query_value / set_query_value; 0.10.0+ streamtree.crud, helpers.explore, enterprise, perf, DeferredFragment, memo_subtree. See docs/PERFORMANCE.md and docs/PHASE3_CRUD.md.
  • Quality — Pydantic v2 in the default install, typing-first APIs, render_to_tree, and 0.10.0+ summarize_tree_kinds for structural tests.

Optional extras: [tables] pins streamlit-aggrid (+ pandas); [charts] pins plotly, altair, and streamlit-echarts (0.10.0+). [ui] and [auth] pin streamlit-extras and streamlit-authenticator (0.6+). [cli] adds Typer and the streamtree console script (run, doctor, init with --with-pages and 0.10.0+ --template / -t). [asyncio] / [async], [pages], and [runner] remain metadata-oriented. See Dependency strategy. The streamtree.asyncio module and streamtree.helpers (pages, runner, scaffold, explore) ship in the default install; import streamtree exposes streamtree.helpers on the root package.

Requirements

Python 3.10+, with Streamlit ≥ 1.33 (for st.dialog / st.popover overlays and the current test matrix), Pydantic v2, and typing-extensions (see pyproject.toml). PageLink continues to require Streamlit’s multipage APIs from the 1.30+ line; the floor is 1.33 for the whole package as of 0.6.0+.

Installation

pip install streamtree==0.10.0
pip install "streamtree[cli]"   # Typer + ``streamtree run`` / ``doctor`` / ``init``
pip install "streamtree[auth]"  # streamlit-authenticator
pip install "streamtree[ui]"    # streamlit-extras wrappers
pip install "streamtree[tables]"  # streamlit-aggrid + ``DataGrid``
pip install "streamtree[charts]"  # plotly + altair + streamlit-echarts + ``Chart`` / ``AltairChart`` / ``EChartsChart``

From a clone, with dev dependencies:

git clone https://github.com/streamtree-dev/streamtree.git
cd streamtree
uv sync --extra dev
# or: pip install -e ".[dev]"

Quick start

from streamtree import component, render
from streamtree.elements import Button, Card, Page, Text
from streamtree.state import state


@component
def Counter():
    count = state(0)
    return Card(
        Text(f"Count: {count()}"),
        Button("Increment", on_click=lambda: count.increment(1)),
        Button("Reset", on_click=lambda: count.set(0)),
    )


if __name__ == "__main__":
    render(Page(Counter()))

Run examples from the repository root:

streamlit run examples/counter.py
streamlit run examples/routed_app.py
streamlit run examples/app_shell.py
streamlit run examples/async_bg.py
streamlit run examples/model_form.py
streamlit run examples/numeric_nav_demo.py
streamlit run examples/pages_helpers_demo.py
streamlit run examples/overlay_demo.py
streamlit run examples/auth_demo.py
streamlit run examples/datagrid_demo.py
streamlit run examples/datagrid_selection_demo.py
streamlit run examples/chart_demo.py
streamlit run examples/altair_chart_demo.py
streamlit run examples/echarts_demo.py
streamlit run examples/async_loader_demo.py
streamlit run examples/crud_pattern_demo.py
streamlit run examples/crud_automation_demo.py
streamlit run examples/deferred_region_demo.py
streamlit run examples/async_ordered_loader_demo.py
streamlit run examples/phase2_layout_demo.py
streamlit run examples/phase2_composite_demo.py
# With Typer installed (``pip install "streamtree[cli]"``):
streamtree run examples/counter.py

Using Streamlit inside components

The @component body runs on every rerun in the same process as streamlit. You can call st.columns, st.metric, plotting APIs, or components.v1 before returning elements. Use stable key= arguments on imperative widgets when Streamlit requires them. When a subtree is drawn entirely with st.*, return fragment().

import streamlit as st

from streamtree import component, fragment, render
from streamtree.elements import Button, Markdown, Page, TextInput, VStack
from streamtree.state import state


@component
def DashboardHeader():
    band, meta = st.columns([3, 1])
    with band:
        st.title("Operations")
    with meta:
        st.metric("Queue depth", 12, delta=-2)

    notes = state("", key="header_notes")
    return VStack(
        Markdown("**Notes** (StreamTree `TextInput`):"),
        TextInput("Session notes", value=notes),
        Button("Clear notes", on_click=lambda: notes.set("")),
    )


@component
def MetricsStrip():
    cols = st.columns(4)
    for i, col in enumerate(cols):
        with col:
            st.metric(f"M{i + 1}", 100 + i * 7, delta=i - 1)
    return fragment()


if __name__ == "__main__":
    render(Page(DashboardHeader(), MetricsStrip()))

App shell, theme, and background tasks

App plus render_app() centralize page configuration. Combine ThemeRoot with provider(theme=Theme(...)) for CSS variables, and use streamtree.asyncio.submit for non-blocking work you observe via TaskHandle.status (and optionally TaskHandle.progress()) on reruns. From inside the worker, call streamtree.asyncio.set_task_progress with the same key you passed to submit.

from streamtree import asyncio, component, render_app
from streamtree.app import App
from streamtree.app_context import provider
from streamtree.elements import Page, Text, ThemeRoot, VStack
from streamtree.theme import Theme


@component
def Body():
    handle = asyncio.submit(lambda: 7, key="demo_job")
    return VStack(
        ThemeRoot(),
        Text(f"status={handle.status()} progress={handle.progress()!r} result={handle.result()}"),
    )


if __name__ == "__main__":
    with provider(theme=Theme(primary_color="#0068c9")):
        render_app(App(page_title="Demo", body=Body()))

Patterns

Grid of components

from streamtree.elements import Grid

Grid(UserCard(user1), UserCard(user2), columns=2)

Bound text input

from streamtree.elements import TextInput
from streamtree.state import state

search = state("")
TextInput(label="Search", value=search)

Multipage discovery (0.5+; page_links in 0.8.0)

Use streamtree.helpers.pages.discover_pages(__file__) to list scripts under Streamlit’s pages/ folder next to your entry script. Each PageEntry has label and page for PageLink. Use streamtree.helpers.page_links(...) to turn discovery rows into PageLink tuples for SidebarNav or custom shells. See examples/pages_helpers_demo.py.

Documentation

Resource Description
Plan Vision, architecture, and dependency philosophy
Roadmap Phased delivery and release index
Phase 2 tail Grooming after 0.6.0 (navigation, asyncio, forms)
Dependency strategy Optional extras, default-install helpers (runner, pages, scaffold), exploration + enterprise modules (0.10.0+), and CI typing notes
Performance memo / memo_subtree, background work (submit_many, match_task_many, submit_many_ordered), URL filters, optional [tables] / [charts], large-tree guidance, DeferredFragment, streamtree.perf + summarize_tree_kinds (0.10.0+)
Phase 3 CRUD patterns List/detail/save with DataGrid, streamtree.crud URL + save-intent helpers, async + match_task_many, streamtree init --template shells
Phase 2 portals / prefetch Portals, prefetch, form-layout semantics (Phase 2 contract)
Phase 2 form layout forms_layout, build_model_from_bindings, and bool-aware grids
CHANGELOG Release history (e.g. 0.10.0 Phase 3 slice; 0.9.0 Phase 2; 0.8.0 data toolkit)

Contributing

Install dev tools, then run lint, type check, and tests (mirrors CI on Python 3.10–3.13):

uv sync --extra dev
uv run ruff format .           # apply formatting locally
uv run ruff format --check .   # same check CI runs (no file writes)
uv run ruff check src tests
uv run ty check src
uv run pytest                  # 100% line coverage on ``src/streamtree`` (see ``pyproject.toml``)
uv run python -m mkdocs build --strict   # static docs site (see mkdocs.yml)
uv build

Equivalent with pip: pip install -e ".[dev]", then ruff, ruff format / ruff format --check, ty check src, pytest, mkdocs build --strict, and uv build (or python -m build) as above.

Releases

Before tagging v0.10.0 (or any v*.*.* release), confirm uv build succeeds, uv run pytest passes with coverage, and pyproject.toml [project].version, CHANGELOG.md, and streamtree.__version__ (installed package metadata, asserted against pyproject.toml in tests/test_package_meta.py) all agree on the version.

Automated: Add a PYPI_API_TOKEN secret to the repository. When tests pass on a v*.*.* tag, the release workflow runs lint, type check, pytest (including coverage), builds with uv build, publishes to PyPI, and publishes the same dist/ artifacts to GitHub Packages for this repository (using the workflow GITHUB_TOKEN with packages: write). Open the repository Packages page to browse published versions.

Manual: uv build (or python -m build), then upload dist/ with twine or uv publish to PyPI and/or GitHub Packages. Bump [project].version in pyproject.toml and CHANGELOG.md; streamtree.__version__ follows installed metadata from that version after rebuild or reinstall.

Imperative handles (limits)

Streamlit does not expose stable cross-rerun APIs for arbitrary widget focus, scroll position, or DOM-level control. Portable patterns: drive UI via st.session_state, query params (streamtree.routing.sync_route, sync_query_value, set_query_value, update_query_params, clear_query_param, clear_route), st.rerun, and element key= discipline. Portal / PortalMount move subtrees between shell regions within the same script rerun; they do not escape Streamlit’s single-page execution model. See docs/PHASE2_PORTALS_AND_PREFETCH.md.

License

MIT. See LICENSE.

Project details


Download files

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

Source Distribution

streamtree-0.10.0.tar.gz (136.4 kB view details)

Uploaded Source

Built Distribution

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

streamtree-0.10.0-py3-none-any.whl (63.4 kB view details)

Uploaded Python 3

File details

Details for the file streamtree-0.10.0.tar.gz.

File metadata

  • Download URL: streamtree-0.10.0.tar.gz
  • Upload date:
  • Size: 136.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for streamtree-0.10.0.tar.gz
Algorithm Hash digest
SHA256 fb17a46b7d8fe8f8c91d75364b3364b022075702bc102f18e66dfefb5877310c
MD5 3b88e7c135dd755216cdf830e2f51469
BLAKE2b-256 4d16c7130a5ce0071b9b6bed69e7f40acf46f44b50c527d2331d340d0c8c6dec

See more details on using hashes here.

File details

Details for the file streamtree-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: streamtree-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 63.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for streamtree-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4e27b64f85fc6ef52076b785c13ef2a55a5a832efefd16609ba26570f89a3a7
MD5 096e73c8eb286f52b676e422203f1f30
BLAKE2b-256 a7116d32cc69f23b5bf038e247d3129a29581922b83bd59890ec6bfaf683b6a3

See more details on using hashes here.

Supported by

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