Stlite is a serverless Streamlit running in browsers via Pyodide (Python-to-WebAssembly). No backend server needed.
- Stack: React + TypeScript + Vite + Pyodide(Python)
- Monorepo: Yarn workspaces, Makefile-orchestrated builds
- Testing: Vitest + Playwright
- Streamlit fork: Git submodule at
streamlit/(forked customized branch namedstlite-<version>)
packages/
common/ # Shared TS types (base package, no deps)
kernel/ # Core Pyodide runtime & worker management
py/stlite-lib/ # Python: custom Streamlit server (replaces streamlit.web)
py/streamlit/ # Compiled Streamlit wheel for Pyodide
react/ # React bindings (StliteApp, StliteAppWithToast)
browser/ # Browser-mountable API (mount(), <streamlit-app>)
desktop/ # Electron desktop app
sharing/ # App sharing service
sharing-editor/ # Editor app for the sharing service (Monaco editor)
sharing-common/ # Shared code for sharing (protobuf)
tooling/ # Build utilities
streamlit/ # Git submodule: whitphx's Streamlit fork
docs/ # Documentation site (Astro Starlight), see below
.make/ # Sentinel files for incremental builds
Dependency graph: common -> kernel -> react -> browser / desktop / sharing
See CONTRIBUTING.md for environment setup, dev workflows per package, and building.
Key points for AI assistants:
- Always use Makefile targets (
make browser,make kernel, etc.) - they handle dependency ordering via sentinel files in.make/. - Do NOT run
cd packages/X && yarn builddirectly - dependencies may not be built. NODE_OPTIONS="--max-old-space-size=6144"is set in Makefile to prevent heap errors.
# Unit tests (Vitest) - per package
cd packages/<pkg> && yarn test
cd packages/<pkg> && yarn test:watch
make kernel-test # With proper build deps
# E2E tests (Playwright)
cd packages/browser/e2e-tests
yarn install && yarn install:browsers && yarn test
# Python tests (pytest)
cd packages/kernel/py/stlite-lib && uv run pytest- Vitest uses
jsdomenvironment (not happy-dom, due to iframe issues) - Prefer
make kernel-testfor kernel tests (it builds required wheels). Runmake stlite-lib-wheel streamlit-wheelmanually only if you callcd packages/kernel && yarn testdirectly.
# TypeScript
cd packages/<pkg>
yarn check:lint # or yarn fix:lint (oxlint)
yarn check:format # or yarn fix:format (oxfmt)
# Python (stlite-lib)
cd packages/kernel/py/stlite-lib
uv run ruff check . && uv run ruff format . && uv run pyright- Lint: oxlint (
.oxlintrc.jsonat repo root), Format: oxfmt (.oxfmtrc.json, 2 spaces, semicolons, printWidth 80) - Unused vars/args with
_prefix are allowed repo-wide (argsIgnorePattern: "^_"etc.) - Git hooks (Husky): pre-commit runs lint-staged (oxfmt on all formatter-supported files, oxlint --fix on JS/TS)
- Commits: conventional format
type(scope): summary
Each PR that introduces user-facing changes (new features, bug fixes, breaking changes) MUST include a Changeset file created via yarn changeset. This ensures proper versioning and changelog generation.
yarn changesetFor AI assistants: After implementing any meaningful user-facing change (new feature, bug fix, dependency update like a Streamlit rebase, breaking change), ask the user whether a changeset fragment should be added so it isn't forgotten before the PR is opened.
Worker-based: Python runs in Web Workers (Dedicated or SharedWorker) via Pyodide. Main thread handles React UI, communicates via postMessage.
Streamlit integration: Stlite uses streamlit.runtime but replaces streamlit.web (Tornado) with stlite-lib (custom server at packages/kernel/py/stlite-lib/stlite_lib/). Key files: server/Server.py, codemod.py (AST transforms).
File systems: MEMFS (default, ephemeral), IDBFS (persistent via IndexedDB), NODEFS (desktop only).
Astro Starlight site with pages for @stlite/browser, @stlite/react, and @stlite/desktop. Uses a single source of truth approach: demo files and package versions are imported directly from the workspace packages, not duplicated.
The demos/ directories in packages/browser/ and packages/react/ serve triple purpose:
- Local dev entry points —
packages/*/index.htmlis the Vite dev server entrypoint (served byyarn start). Demo apps should be linked from it so developers can navigate to them during local development. - E2E test fixtures — Built into test-ready pages and served during Playwright tests. The E2E tests are in
packages/*/e2e-tests/. - Documentation examples — Imported as raw content into
docs/MDX pages with CDN URLs substituted via{{STLITE_JS_URL}}/{{STLITE_CSS_URL}}placeholders. The documentation sources are indocs/src/content/.
Each E2E suite has a playwright.config.ts that defines HTTP servers, browser projects, and timeouts. Both suites use extended timeouts to account for Pyodide loading time. See each config for details.
The @stlite/react E2E suite uses Playwright's toHaveScreenshot() for visual regression testing. Snapshot baselines must be generated on Linux to match CI.
cd packages/react/e2e-tests
# 1. Build demo pages (must include any new demos added to vite.demo.config.ts)
yarn build:demo
# 2. Generate/update snapshots in a Docker container (Linux-consistent rendering)
yarn test:docker:update-snapshots
# 3. Commit the generated/updated .png files from snapshots/- Scripts:
scripts/update-snapshots-linux.shcallsscripts/run-in-docker.sh --update-snapshots, which runs Playwright insidemcr.microsoft.com/playwrightDocker image. - Snapshot location:
packages/react/e2e-tests/snapshots/ - Demo build output:
packages/react/e2e-tests/demo-dist/(built fromvite.demo.config.ts) - When adding a new demo with screenshots: Add it to
vite.demo.config.tsrollup inputs, runyarn build:demo, then runyarn test:docker:update-snapshotsto generate the baseline image.
MUST match Pyodide: Python version specified in .python-version and enforced by the Makefile must match the version used in the Pyodide runtime (e.g. Python 3.12 for Pyodide 0.28.0). This ensures compatibility of built wheels and prevents runtime errors.
- Only pure Python or pre-built-for-Pyodide packages work
- Check: https://pyodide.org/en/stable/usage/packages-in-pyodide.html
time.sleep()is NO-OP - useawait asyncio.sleep()instead- Top-level
awaitIS supported in Stlite (unlike standard Streamlit) - Use async generators for
st.write_stream()
Never import streamlit.web in stlite-lib (Tornado can't run on Pyodide). Use stlite_lib.server.Server instead. streamlit.runtime is safe.
Known to work in this repo: requests, urllib, urllib3, pyodide.http.pyfetch(). Many other networking libraries (e.g., httpx, aiohttp) may not be compatible with Pyodide—verify against Pyodide docs and this repo before using them.
- Sentinel files in
.make/track build state - always@touch $@in Makefile targets - Wheel filenames include Python version - use
@stlite/tooling get-streamlit-wheel-file-name - Build order matters: use
make <target>, notyarn builddirectly
Add JS dependency: cd packages/<pkg> && yarn add <pkg>, then make <target> from root.
Add Python dependency: cd packages/kernel/py/stlite-lib && uv add <pkg>, then make stlite-lib-wheel.
Update Streamlit / sample apps / release: See CONTRIBUTING.md.
Pre-PR checklist: make kernel-test, E2E tests, yarn tsc --noEmit, yarn check:lint, yarn check:format, yarn changeset.