Hot reload for local development
Hot reload here means file-watched process restart, not in-process module hot-swap. stario watch restarts the server when matched files change, using the same MODULE:bootstrap entry as stario serve.
uv run stario watch main:bootstrapExamples below omit uv run when stario is already on your PATH; otherwise keep uv run stario …. That keeps one composition root—your bootstrap(app, span)—and lets the CLI own process lifecycle and reload.
Watch paths (--watch / --watch-ignore)
watch uses watchfiles (install it if the CLI tells you to). You control what triggers a restart:
--watch— add a file, directory, or glob (repeat the flag for multiple roots). If you omit--watch, the CLI watches the current directory (.).--watch-ignore— exclude paths or globs (repeatable). SQLite database files are ignored by default (*.sqlite3,*.db, …).
Examples (same as the CLI epilog):
stario watch main:bootstrap --watch app/stario watch main:bootstrap --watch main.pystario watch main:bootstrap --watch '**/*.py' --watch-ignore 'data/'stario watch main:bootstrap --watch-ignore '*.sqlite3'End directories with / when the folder does not exist yet (so the watcher can still attach to the parent). All other serve / watch flags (--host, --port, --tracer, …) apply the same way as stario serve.
Example
A useful development-only pattern: keep a long-lived SSE connection (for example a dev stream or a Datastar GET that stays open). When watch restarts the server, that connection drops. On the new process, you can handle the next client connection by streaming a full HTML document (from <html> through </html>) over SSE using ds.sse.patch_elements with selector="html" and mode="outer".
You need all of the following before the one-liner below makes sense:
A route that holds an SSE response open—usually
async with w.alive():around your send loop (Responses —alive()).After reload, the next connection should receive a freshly rendered full HTML document (the same shell you would return from a normal page
GET) so the client can resync without a manual refresh.full_pagein the snippet is that document:bytes,str, or HTML nodes for<html>…</html>.
from stario import datastar as ds # full_page: bytes/str or html nodes for the whole document (<html>…</html>).# Replaces the root so the tab resyncs without a manual refresh after a dev reload.ds.sse.patch_elements(w, full_page, selector="html", mode="outer")Wire this only where it makes sense (for example a dev-only route or a flag in bootstrap). It is not a substitute for normal navigation or production error recovery.
Use mindfully:
Scope — Treat this as a dev ergonomics trick, not a product feature, unless you fully own the UX implications.
Surprise — Replacing the whole document can reset client state, scroll position, and focus; only do it when you intend a full resync after reload.
Production — Do not depend on “SSE replaces the world” for real users without the same care you’d give any hot reload or live-edit system; prefer ordinary page loads or targeted patches for shipped behavior.
Related
Runtime — Server — how
Serverrunsbootstrap.Datastar —
patch_elements, SSE helpers,Writer.alive().Realtime tiles — a full project layout to grow from.