This guide is for the people who run, deploy, secure, extend, and maintain Streamlit applications — operators, platform engineers, architects, and contributors. It documents Streamlit's internal architecture, the complete configuration surface, the security model, deployment strategies, and the code-level internals of the framework.
The companion User Guide covers the st.* authoring API. This document assumes you already understand what a Streamlit app is and focuses on what happens behind the curtain and how to operate it.
Note
This guide documents Streamlit 1.58.0 (
streamlitpackage), built directly from source. Streamlit is an open-source (Apache-2.0) Python framework maintained by Snowflake Inc. A defining fact of this release line: the web server has moved from Tornado to Starlette/Uvicorn (ASGI), which underpins the newst.Appembedding model and several operational behaviors below.
1. Executive Summary
Streamlit turns a plain Python script into a reactive web app. Architecturally it is a two-process system bridged by a typed message protocol:
- A Python backend (the
streamlitlibrary + a Starlette/Uvicorn server) executes the user's script and emits UI updates. - A React/TypeScript frontend (compiled and bundled, shipped as static assets) renders those updates in the browser.
- The two communicate over a WebSocket carrying Protocol Buffers messages —
ForwardMsg(server → browser) andBackMsg(browser → server).
The central runtime idea is the rerun loop: any user interaction sends a BackMsg rerun request; the backend re-executes the script top to bottom; each st. call produces a delta appended to a ForwardMsg queue; the frontend applies the deltas to a render tree. This makes app code trivially simple at the cost of re-execution, which is mitigated by caching and fragments.
| Attribute | Value |
|---|---|
| Language (backend) | Python 3.10–3.14 |
| Language (frontend) | TypeScript + React + Emotion (CSS-in-JS), built with Vite |
| Web server | Starlette on Uvicorn (ASGI); httptools + websockets |
| Wire protocol | Protocol Buffers over WebSocket |
| License | Apache-2.0 |
| Version | 1.58.0 |
| Entry point | streamlit = streamlit.web.cli:main |
| Default port | 8501 |
2. Architecture
2.1 High-level component diagram
Browser (React SPA + Emotion + Vite-built assets)
▲ │
ForwardMsg (deltas)│ │ BackMsg (rerun / widget / file)
protobuf │ ▼ protobuf
┌─────────────────────────────────────────────┐
│ Starlette / Uvicorn ASGI server │
│ /_stcore/stream (WebSocket) │
│ /_stcore/health /_stcore/host-config │
│ /_stcore/upload_file /media /static │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Runtime │
│ SessionManager → AppSession (per browser) │
│ │ │
│ ▼ │
│ ScriptRunner ── runs your app script ──► │
│ │ (top-to-bottom each rerun) │
│ ▼ │
│ DeltaGenerator → ForwardMsgQueue │
│ MediaFileManager · UploadedFileManager │
│ Caching (cache_data / cache_resource) │
│ SessionState · Secrets │
└─────────────────────────────────────────────┘
2.2 The request/rerun lifecycle
- The browser opens a WebSocket to
/_stcore/stream. - The server creates an
AppSessionfor that connection (via theSessionManager). - On connect and on every rerun, the server sends a
NewSessionForwardMsgdescribing the app, config, and theme. - The
ScriptRunnerexecutes the user's script in a dedicated thread. Eachst.call writes a delta (anElementorBlockprotobuf) onto the session'sForwardMsgQueue. - The queue is flushed to the browser as
ForwardMsgdeltas; the frontend applies them to its render tree in order. - A user interaction sends a
BackMsg(rerun request, widget state, or page change). TheScriptRunnerreruns the script with the new widget values, and the cycle repeats.
Note
Because the script is re-executed wholesale on each interaction, the framework's performance story rests on three mechanisms: the ForwardMsg cache (large messages above
global.minCachedMessageSizeare cached and referenced by hash),cache_data/cache_resourcefor app-level memoization, and fragments (@st.fragment) which let a sub-region rerun in isolation.
2.3 Repository / package layout
lib/ Python backend
streamlit/ The installable `streamlit` package
__init__.py Public API surface (the st.* namespace)
delta_generator.py DeltaGenerator — emits element deltas
config.py Configuration system (all option definitions)
elements/ Backend of display elements & charts
widgets/ Backend of input widgets
lib/ Shared element helpers (columns, dialogs, …)
commands/ echo, logo, navigation, page_config, execution_control
runtime/ Runtime, AppSession, ScriptRunner, caching, state, secrets,
fragment, connection_factory, media/uploaded-file managers
web/ CLI + server
cli.py The `streamlit` CLI (click)
server/ Starlette server, app discovery, routes
connections/ SQL / Snowflake / Snowpark connection classes
navigation/ Multipage navigation (Page)
proto/ Generated protobuf Python classes
frontend/ TypeScript/React frontend
app/ The Streamlit application shell (the SPA)
lib/ Shared component library (elements, widgets, layouts)
connection/ WebSocket connection handling
utils/ Shared utilities
component-lib/ Custom components v1 SDK
component-v2-lib/ Custom components v2 SDK
proto/streamlit/proto/ Protobuf message definitions (source of truth)
e2e_playwright/ End-to-end tests (Playwright via pytest)
specs/ Internal product/tech specs
wiki/ Development documentation
2.4 The protobuf protocol
The frontend/backend contract is defined in proto/streamlit/proto/. The key messages:
| Message | Direction | Role |
|---|---|---|
ForwardMsg |
server → browser | Root message carrying deltas, session info, and control signals. |
BackMsg |
browser → server | Root message for rerun requests, widget states, page changes, file ops. |
NewSession |
server → browser | Sent on every rerun; carries app config, theme, and page metadata. |
Block |
server → browser | Layout containers (columns, tabs, expander, popover, form, sidebar). |
Element |
server → browser | A single UI element (one per st. display call). |
Important
The protobuf layer is the reason
api_endpointsstatic analysis finds "no REST API" — Streamlit's real interface is a binary WebSocket protocol, not HTTP routes. Treatproto/streamlit/proto/*.protoas the authoritative interface spec when reasoning about client/server compatibility. Protobuf changes require recompilation (make protobuf) for both Python and the frontend.
2.5 The st.App ASGI model
st.App (re-exported from streamlit.web.server.starlette.starlette_app) wraps a Streamlit app as a standard ASGI application:
from streamlit import App
app = App("main.py", secrets={"api_key": "..."}, lifespan=my_lifespan,
routes=extra_routes, middleware=extra_middleware)
This enables three deployment modes (tracked internally as the server mode metric):
starlette-managed— the classicstreamlit runpath (Streamlit owns the server).starlette-app/asgi-server—st.Apprun under uvicorn/gunicorn/hypercorn.asgi-mounted—st.Appmounted inside another ASGI app (FastAPI/Starlette).
Warning
st.Appis experimental. A hard constraint: the firstAppconstructed in a process pins the script-level config directory via the process-globalconfig._main_script_path. Running multipleAppinstances with differentscript_pathvalues in one process is unsupported and will mis-resolve relative paths.
3. System Requirements
| Layer | Requirement |
|---|---|
| Python | 3.10, 3.11, 3.12, 3.13, or 3.14 |
| Core dependencies | altair, blinker, cachetools, click, gitpython, numpy, packaging, pandas, pillow, pydeck, protobuf (>=3.20,<8), pyarrow (>=7), requests, tenacity, toml, typing-extensions, starlette (>=0.40), uvicorn (>=0.30), httptools, anyio, python-multipart, websockets, itsdangerous, watchdog (non-macOS) |
| Optional extras | snowflake, pdf, auth (Authlib), charts (matplotlib/graphviz/plotly/orjson), sql (SQLAlchemy), performance (orjson/uvloop), all |
| Browser | Any modern evergreen browser (Chrome, Firefox, Safari, Edge). |
Note
Streamlit deliberately keeps its dependency list small and version-bounded.
pandasis pinned by Python version (2.x on 3.10, 3.x on 3.11+). On macOS,watchdogis omitted and Streamlit falls back to a polling file watcher.
4. Configuration Mastery
4.1 Configuration sources and precedence
Options are resolved in this order, each overriding the previous (from config.get_config_options):
- Built-in defaults (defined in
lib/streamlit/config.py) - Global
~/.streamlit/config.toml - Per-project
$CWD/.streamlit/config.toml - Environment variables (
STREAMLIT_<SECTION>_<OPTION>, e.g.STREAMLIT_SERVER_PORT) - Command-line flags to
streamlit run(e.g.--server.port 8080)
Inspect the resolved state with streamlit config show. A config value of the form "env:VARNAME" in a TOML file is replaced with that environment variable's value at parse time.
Important
Sensitive options (
server.cookieSecret,mapbox.token) cannot be set via CLI flag — attempting it exits with an error. Set them inconfig.tomlor via their environment variable. Changing any[server]option requires a server restart to take effect.
4.2 [server] — the operational core
| Option | Default | Purpose |
|---|---|---|
server.port |
8501 |
Port the server listens on. |
server.address |
unset | Bind address. If set, the server is reachable only at this address. |
server.baseUrlPath |
"" |
Serve the app under a sub-path (e.g. behind a proxy). |
server.headless |
auto* | If false, opens a browser on start. *Defaults true on Linux without DISPLAY/WAYLAND_DISPLAY. |
server.runOnSave |
false |
Auto-rerun when the script file changes. |
server.fileWatcherType |
"auto" |
auto / watchdog / poll / none. |
server.folderWatchList |
[] |
Extra absolute directories to watch. |
server.folderWatchBlacklist |
[] |
Directories to ignore. |
server.maxUploadSize |
200 |
Max upload size (MB) for st.file_uploader. |
server.maxMessageSize |
200 |
Max WebSocket message size (MB). |
server.enableCORS |
true |
CORS protection. |
server.corsAllowedOrigins |
[] |
Allowed origins when CORS is enabled. |
server.enableXsrfProtection |
true |
CSRF protection via signed cookie. |
server.cookieSecret |
random | Sensitive. Symmetric key for signing cookies; set identically across replicas. |
server.enableWebsocketCompression |
false |
WebSocket compression. |
server.websocketPingInterval |
unset | Keep-alive ping interval (s); tune for flaky proxies. |
server.enableStaticServing |
false |
Serve files from a static/ directory. |
server.disconnectedSessionTTL |
120 |
Seconds a disconnected session's state is retained. |
server.sslCertFile / server.sslKeyFile |
unset | HTTPS cert/key. Not for production (see Security). |
server.showEmailPrompt |
true |
First-run email prompt (suppressed when headless). |
server.trustedUserHeaders |
{} |
(experimental, hidden) Map HTTP headers into st.user. |
4.3 [client] — browser-side behavior (runtime-settable)
| Option | Default | Purpose |
|---|---|---|
client.showErrorDetails |
"full" |
How much error detail reaches the browser: full / stacktrace / type / none. |
client.toolbarMode |
"auto" |
Toolbar/menu visibility: auto / developer / viewer / minimal. |
client.showSidebarNavigation |
true |
Show auto page nav for pages/-based apps. |
client.showErrorLinks |
"auto" |
Show Google/ChatGPT help links on errors (auto = localhost only). |
client.allowedOrigins |
CC defaults | (hidden) Origins allowed to control the app via postMessage when iframed. |
Tip
client.*options are the only ones settable at runtime viast.set_option(...)from within a script. Everything else must be set via config file, env var, or CLI flag.
4.4 [runner] — script execution
| Option | Default | Purpose |
|---|---|---|
runner.magicEnabled |
true |
Auto-display bare variables/literals. |
runner.fastReruns |
true |
Handle rerun requests immediately (more responsive; minor race-condition risk for state mutated outside explicit assignment). |
runner.postScriptGC |
true |
(hidden) Run GC after each script execution. |
runner.enforceSerializableSessionState |
false |
Raise if non-serializable data is added to session state. |
runner.enumCoercion |
"nameOnly" |
How Enum members coerce across reruns: off / nameOnly / nameAndValue. |
runner.parallelMaxWorkers |
auto | Max parallel fragment worker threads per run. |
4.5 [browser], [logger], [magic], [ui], [global]
| Option | Default | Purpose |
|---|---|---|
browser.serverAddress |
"localhost" |
Address users point their browser at (CORS/XSRF, terminal URL). |
browser.serverPort |
= server.port |
Port shown in the URL. |
browser.gatherUsageStats |
true |
Send anonymous usage stats to Streamlit. |
logger.level |
"info" (debug in dev) |
Internal log level: error / warning / info / debug. |
logger.messageFormat |
%(asctime)s %(message)s |
Log format string. |
logger.enableRich |
auto | (hidden) Use rich for exception tracebacks if installed. |
logger.hideWelcomeMessage |
false |
(hidden) Suppress the startup welcome/URL banner. |
global.developmentMode |
auto | True only when running from a source checkout. |
global.disableWidgetStateDuplicationWarning |
false |
Silence the value-vs-session-state warning. |
magic.displayRootDocString / magic.displayLastExprIfNoSemicolon |
false |
(hidden) Notebook-like magic behaviors. |
4.6 [theme] — appearance
The [theme] section controls the visual theme and is inheritable. theme.base can be "light", "dark", or a path/URL to a TOML theme file. You can override per-mode and per-region with [theme.sidebar], [theme.light], [theme.dark], [theme.light.sidebar], and [theme.dark.sidebar] (only these nestings are valid — an invalid nesting raises StreamlitInvalidThemeSectionError).
| Group | Options (selection) |
|---|---|
| Colors | primaryColor, backgroundColor, secondaryBackgroundColor, textColor, linkColor, borderColor, plus a full named palette (redColor/blueColor/… with matching *BackgroundColor and *TextColor) |
| Typography | font, headingFont, codeFont, baseFontSize, baseFontWeight, headingFontSizes, headingFontWeights, codeFontSize, codeFontWeight, metricValueFontSize, metricValueFontWeight, [[theme.fontFaces]] |
| Shape | baseRadius, buttonRadius (none/small/medium/large/full/px/rem) |
| Tables | dataframeBorderColor, dataframeHeaderBackgroundColor, showWidgetBorder, showSidebarBorder |
| Charts | chartCategoricalColors, chartSequentialColors, chartDivergingColors |
| Links/code | linkUnderline, codeTextColor, codeBackgroundColor |
[theme]
base = "light"
primaryColor = "#0068c9"
font = "sans-serif"
baseRadius = "medium"
[[theme.fontFaces]]
family = "MyFont"
url = "app/static/myfont.woff2"
weight = "400"
4.7 [secrets] and [mapbox]
secrets.fileslists thesecrets.tomllocations searched (global, project, then script-level; later wins). Access viast.secrets.mapbox.tokenis deprecated (expired 2026-05-01) — use theMAPBOX_API_KEYenvironment variable or PyDeck'sapi_keysargument instead.
Caution
Streamlit warns and auto-corrects a known incompatible combination:
server.enableXsrfProtection=truewithserver.enableCORS=false. When detected, CORS is forced back on, because XSRF protection relies on origin restrictions.
5. Security
Streamlit's security posture centers on protecting the WebSocket channel, the upload path, and (optionally) authenticating users.
5.1 CSRF / XSRF and CORS
- XSRF protection (
server.enableXsrfProtection=true, default on) sends a signed cookie with each request, signed usingserver.cookieSecret. - CORS protection (
server.enableCORS=true, default on) restricts cross-origin traffic; pair withserver.corsAllowedOrigins. - The two are linked: disabling CORS while XSRF is on causes Streamlit to re-enable CORS.
5.2 Cookie secret and multi-replica deployments
server.cookieSecret defaults to a random per-process value. In any multi-replica deployment you must set the same cookieSecret across all replicas, or signed cookies (and therefore sessions and auth) will break on replica switches. It is a sensitive option — set it via config.toml or the STREAMLIT_SERVER_COOKIE_SECRET environment variable, never on the CLI.
5.3 TLS / HTTPS
server.sslCertFile and server.sslKeyFile enable HTTPS directly.
Caution
The built-in SSL options carry an explicit in-code warning: DO NOT USE in production. They have not been security- or performance-audited. For production, terminate TLS at a load balancer or reverse proxy and run Streamlit behind it over plain HTTP on the internal network.
5.4 Authentication (OIDC)
With the auth extra, st.login / st.logout / st.user provide OpenID Connect login. Credentials live in .streamlit/secrets.toml under [auth] and must include redirect_uri, cookie_secret, and per-provider client_id/client_secret. Missing/malformed config raises descriptive StreamlitAuthErrors. Provider names must not contain underscores. Tokens are validated for provider/exp claims and expiry.
5.5 Upload and message limits
server.maxUploadSize (200 MB) and server.maxMessageSize (200 MB) cap file uploads and WebSocket payloads. Lower these in untrusted environments to limit memory pressure and abuse. Uploads flow through /_stcore/upload_file/{session_id}/{file_id} and are tracked by the UploadedFileManager.
5.6 Embedding and untrusted-app hosting
client.allowedOriginscontrols which parent-frame origins can drive the app viapostMessagewhen embedded in an iframe. Because app code can modify this at runtime, platforms hosting untrusted app code should override the/_stcore/host-configendpoint at the proxy level rather than relying on this option.server.enableStaticServingexposes astatic/directory — keep it off unless you intend to serve files.client.showErrorDetails="none"(or"type") keeps tracebacks and full error messages out of the browser; they still print to the server console.
Important
Raw-HTML escape hatches (
st.html,unsafe_allow_html=True,components.v1.html) are XSS vectors if fed untrusted input. Audit any app that renders user-controlled HTML.
6. Operations
6.1 Running and deploying
Self-hosted (classic):
streamlit run app.py \
--server.port 8501 \
--server.address 0.0.0.0 \
--server.headless true \
--browser.gatherUsageStats false
Docker (sketch):
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health || exit 1
ENTRYPOINT ["streamlit", "run", "app.py", \
"--server.port=8501", "--server.address=0.0.0.0", "--server.headless=true"]
ASGI via st.App + uvicorn:
# myapp.py
from streamlit import App
app = App("main.py")
uvicorn myapp:app --host 0.0.0.0 --port 8501 --workers 1
Mounted inside FastAPI/Starlette (asgi-mounted mode): construct st.App and mount it on a sub-path of your FastAPI/Starlette application.
Streamlit Community Cloud: push the repo to GitHub, deploy from share.streamlit.io, set secrets in the dashboard. Free for public apps.
6.2 Health checks and internal endpoints
| Endpoint | Purpose |
|---|---|
GET /_stcore/health |
Liveness/readiness probe (200 = healthy). |
WS /_stcore/stream |
The app WebSocket (ForwardMsg/BackMsg). |
POST /_stcore/upload_file/{session_id}/{file_id} |
File-uploader endpoint. |
GET /_stcore/host-config |
Host configuration (allowed origins, etc.). Override at the proxy for untrusted hosting. |
GET /media/{file_id} |
Media files (images, audio, video). |
GET /static/... |
App static assets / static/ serving. |
Tip
Point your load balancer / orchestrator health probe at
/_stcore/health. For app-script readiness specifically, the hiddenserver.scriptHealthCheckEnabledexposes a script-health endpoint that returns 200 on a successful script load and 503 on failure.
6.3 Scaling and session affinity
Streamlit sessions are stateful and in-memory, tied to a single WebSocket on a single replica.
Important
When running multiple replicas behind a load balancer you must enable session stickiness (affinity), or a reconnecting browser will land on a replica with no knowledge of its session and lose state. Also set a shared
server.cookieSecret.server.disconnectedSessionTTL(120 s) governs how long a replica keeps a disconnected session's state available for reconnection.
6.4 File watching, logging, restarts
- File watching uses
watchdogwhen available, polling otherwise (server.fileWatcherType). Enableserver.runOnSavefor auto-rerun during development. - Control logging with
logger.levelandlogger.messageFormat. Installrichfor syntax-highlighted tracebacks (logger.enableRich). [server]option changes require a restart — Streamlit logs a warning when it detects a server-option change in a re-parsed config file.
6.5 Cache maintenance
streamlit cache clearclears on-diskcache_data/cache_resourcecaches (not in-memory).- Programmatic:
my_cached_func.clear()orst.cache_data.clear()/st.cache_resource.clear(). cache_data(persist="disk")survives restarts; tunettlandmax_entriesto bound memory.
7. Internals (Developer Reference)
This section is for contributors and anyone who needs to reason about Streamlit's behavior at the code level.
7.1 The element pipeline: DeltaGenerator
Every st.<element> call is a method on a DeltaGenerator (DG). The public st.write, st.button, etc. are bound from the singleton _main DG in __init__.py. A DG:
- Builds the element's protobuf (
ElementorBlock). - Appends it to the session's delta path (the render-tree coordinate).
- Enqueues a
ForwardMsg.
Containers (st.columns, st.tabs, st.expander, st.form, st.sidebar, st.container) return child DGs scoped to a Block, which is how nested layouts work. The DG singleton machinery lives in delta_generator_singletons.py to break circular imports between the DG and the elements it dispatches to.
7.2 Runtime, sessions, and the script runner
| Component | File | Responsibility |
|---|---|---|
Runtime |
runtime/runtime.py |
Top-level lifecycle; owns sessions and the media manager. |
SessionManager / WebsocketSessionManager |
runtime/session_manager.py, websocket_session_manager.py |
Create/track AppSessions per WebSocket. |
AppSession |
runtime/app_session.py |
One browser session: state, script lifecycle, message queue. |
ScriptRunner |
runtime/scriptrunner/ |
Executes the user script in a thread; handles rerun requests, stop, fragments. |
ForwardMsgQueue |
runtime/forward_msg_queue.py |
Buffers outgoing deltas; coalesces and flushes to the socket. |
ForwardMsgCache |
runtime/forward_msg_cache.py |
Caches large messages (≥ global.minCachedMessageSize) by hash. |
MediaFileManager / UploadedFileManager |
runtime/media_file_manager.py, uploaded_file_manager.py |
Serve media; receive uploads. |
SessionState |
runtime/state/ |
Backs st.session_state and widget state. |
Secrets |
runtime/secrets.py |
Loads and merges secrets.toml; backs st.secrets. |
Fragment |
runtime/fragment.py |
Implements @st.fragment isolated reruns and run_every. |
7.3 The web layer
web/cli.py defines the click CLI (run, hello, init, config show, cache clear, skills, docs, version, activate). Each config option is auto-injected as a --section.option flag by the configurator_options decorator. web/server/app_discovery.py inspects the target script to detect whether it's a plain Streamlit script or an ASGI app (st.App/FastAPI/Starlette), routing to bootstrap.run(...) or bootstrap.run_asgi_app(...) accordingly. web/server/starlette/ holds the Starlette routes, WebSocket handler, gzip and path-security middleware, auth routes, and static serving.
7.4 Connections
runtime/connection_factory.py implements st.connection, returning cached connection objects. Built-in BaseConnection subclasses live in connections/: SQLConnection (SQLAlchemy), SnowflakeConnection, SnowparkConnection, plus a callers-rights Snowflake variant. Custom connections subclass BaseConnection.
7.5 Frontend
The frontend is a React SPA (frontend/app/) backed by a shared component library (frontend/lib/), styled with Emotion (CSS-in-JS), and built with Vite. The WebSocket client lives in frontend/connection/. Each backend element has a corresponding React component under frontend/lib/src/components/{elements,widgets,layouts}/. Custom-component SDKs are frontend/component-lib (v1) and frontend/component-v2-lib (v2). Theming and all pixel/rem layout calculations are performed on the frontend — the backend passes only semantic data, never resolved theme values.
7.6 Testing
- Python unit tests (
lib/tests/, pytest) — internal behavior without the frontend; target ≥95% coverage. - Frontend unit tests (Vitest + React Testing Library) — co-located
*.test.tsx. - E2E tests (
e2e_playwright/, Playwright via pytest) — full-stack, including visual snapshots. - Typing tests (
lib/tests/streamlit/typing/) —mypy assert_typechecks of the public API; never executed at runtime. - The
AppTestframework (runtime/testing/) lets you test apps headlessly in Python.
Note
The repository's
developbranch is the main branch, and it ships maintainer-writtenAGENTS.mdfiles at the root and per-subsystem (lib/,frontend/,proto/streamlit/proto/, etc.) describing build commands and conventions. The build orchestrator ismake(make protobuf,make check,make frontend-fast); all Python tasks run underuv run.
8. Code Quality and Conventions
- Linting/formatting:
ruffwith nearly all rules enabled (select = ["ALL"]), 88-char lines, NumPy docstring convention, relative imports banned. Generated protobuf and vendored code are excluded. - Type checking: strict
mypy(no implicit re-export, disallow untyped defs, warn-unreachable) plus thetychecker. Public-API typing is asserted in dedicated typing tests. - Docstrings: all public
st.*methods use NumPy-style docstrings with executable examples and.. output::directives. - Widget API design: heavy use of
@overloadto give precise return types depending on arguments (e.g.selectboxreturnsT,T | None, orT | strdepending onindex/accept_new_options). The single runtime implementation lives at the end of each overload group. - Backend/frontend split: the backend never computes layout or theme pixels — that is a frontend responsibility by policy.
9. Potential Improvements & Known Issues
Drawn from the project's own issue tracker and the observable state of the codebase at this commit:
- Starlette migration fallout. The move from Tornado to Starlette/Uvicorn is recent and surfaces operational edge cases — e.g. #15407 (page breaks for logged-in users after the Tornado→Starlette conversion) and #15421 (Starlette dependency floor). Operators upgrading from a Tornado-era release should retest auth, proxying, and WebSocket stability.
st.Appis experimental. The single-App-per-process constraint and process-global script-path pinning are sharp edges; expect API changes.- Multithreading limits. Several long-standing requests (#7851, #8490) ask for friendlier multi-threading/multi-processing within an app. Today, session state and the script runner assume a single execution thread per session (fragments add bounded parallelism via
runner.parallelMaxWorkers). - Async caching.
st.cache_data/st.cache_resourcedo not natively cache coroutines (#8308). - Widget-state persistence. Persisting keyed widget state when a widget isn't rendered is requested (#6074).
- Deprecations to track.
mapbox.tokenconfig (expired 2026-05-01; useMAPBOX_API_KEY/PyDeckapi_keys), the deprecatedTrue/Falsevalues forclient.showErrorDetails, and the deprecatedst._bottom(usest.bottom). - SSL is dev-only. The built-in SSL options are explicitly not production-grade; production deployments must terminate TLS upstream.
10. Quick Reference
CLI
streamlit run <target> [-- args] Run an app (file / folder / URL)
streamlit hello Built-in demo
streamlit init [dir] Scaffold a project
streamlit config show Show all config + sources
streamlit cache clear Clear on-disk caches
streamlit skills [-g] [-y] Install AI-agent skills
streamlit docs | version | help
streamlit activate [reset]
Key config defaults
| Option | Default |
|---|---|
server.port |
8501 |
server.maxUploadSize / server.maxMessageSize |
200 MB |
server.enableCORS / server.enableXsrfProtection |
true |
server.disconnectedSessionTTL |
120 s |
client.toolbarMode / client.showErrorDetails |
auto / full |
runner.magicEnabled / runner.fastReruns |
true |
browser.gatherUsageStats |
true |
Internal endpoints
GET /_stcore/health WS /_stcore/stream
POST /_stcore/upload_file/... GET /_stcore/host-config
GET /media/{file_id} GET /static/...
Production checklist
- [ ] Set a shared
server.cookieSecretacross all replicas. - [ ] Enable session stickiness at the load balancer.
- [ ] Terminate TLS at a proxy (do not use built-in SSL).
- [ ] Run
--server.headless trueand--browser.gatherUsageStats false. - [ ] Probe
/_stcore/health. - [ ] Keep
secrets.tomlout of version control; use platform secret stores. - [ ] Set
client.toolbarMode="viewer"andclient.showErrorDetails="none"for public apps. - [ ] Override
/_stcore/host-configat the proxy if hosting untrusted app code.