StreamTree: declarative, typed composition for Streamlit.
Project description
StreamTree
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() 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, and more), and widget wrappers. - App shell (0.3+) —
Appwith a guardedst.set_page_config, plus optional sidebar and main composition viarender_app. - Theming (0.3+) —
Theme,ThemeRoot,theme(),theme_css(), andapp_context.provider(theme=...). - Background work (0.3+) —
streamtree.asyncio.submitandTaskHandlefor stdlib-thread jobs you poll across reruns. - Forms (0.3+) — Pydantic-oriented helpers such as
bind_str_fieldsandstr_text_inputsfor string fields. - State —
state,toggle_state,form_state,memo,cache. - Routing and context — Query-param routing (
streamtree.routing),ErrorBoundary,streamtree.formsutilities, andapp_context.provider/lookupfor shared values. - Interop — Inside
@component, your function body runs during render; you may callst.*(columns, metrics, charts, third-party components) and still return an element tree, orfragment()when the subtree is fully imperative. - Quality — Pydantic v2 in the default install, typing-first APIs, and
render_to_treefor structural tests.
Stub optional extras (tables, charts, ui, auth, asyncio, async, cli) are reserved for future wrappers; see Dependency strategy. The streamtree.asyncio module ships in the default package.
Requirements
Python 3.10+, with Streamlit ≥ 1.28, Pydantic v2, and typing-extensions (see pyproject.toml).
Installation
pip install streamtree==0.3.0
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
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 on reruns.
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()} 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)
Documentation
| Resource | Description |
|---|---|
| Plan | Vision, architecture, risks |
| Roadmap | Phased delivery |
| Dependency strategy | Dependencies and optional extras |
| CHANGELOG | Release history |
Contributing
Install dev tools, then run lint, type check, and tests (mirrors CI on Python 3.10–3.12):
uv sync --extra dev
uv run ruff format .
uv run ruff check src tests
uv run ty check src
uv run pytest
Equivalent with pip: pip install -e ".[dev]", then ruff, ty check src, and pytest as above.
Releases
Automated: Add a PYPI_API_TOKEN secret to the repository. When main is green, push a tag of the form v0.3.0. The release workflow runs lint, type check, pytest (including coverage), builds with uv build, and publishes to PyPI.
Manual: uv build (or python -m build), then upload dist/ with twine or uv publish. Keep pyproject.toml, streamtree.__version__, tests/test_package_meta.py, and CHANGELOG.md in sync when cutting a release.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file streamtree-0.3.0.tar.gz.
File metadata
- Download URL: streamtree-0.3.0.tar.gz
- Upload date:
- Size: 55.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3948837d92efe97cf8fc7efc6d24d8c4ed18cf3170c968e9fdfeb8d612ee48aa
|
|
| MD5 |
8f12ad525b74f80974e386df7f8deadf
|
|
| BLAKE2b-256 |
21f4f200e8a23cf67c332ecfef04b8994fd9cc5d8149acfd588bc5b8ab6394d2
|
File details
Details for the file streamtree-0.3.0-py3-none-any.whl.
File metadata
- Download URL: streamtree-0.3.0-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4109959c7974654d5e13d3052d503df00624acb300b0800759789aa06909fe2b
|
|
| MD5 |
25ae46537cc54debebb476529c122260
|
|
| BLAKE2b-256 |
396cb9994bf6e71ffc9338b891563aeb0dcf2d8152ad0af04645eb31c934ab75
|