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.

bash
uv run stario watch main:bootstrap

Examples 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.

Port and server runtime

Configure listen address, port, tracer, and request limits with STARIO_* environment variables—not CLI flags. If the default port is in use, set STARIO_PORT before starting watch:

bash
STARIO_PORT=8001 stario watch main:bootstrap

Run stario serve --help for the full variable list. Stario does not load .env files; use your shell, direnv, or Compose env_file.

Watch paths (--watch / --watch-ignore)

watch uses watchfiles (bundled with Stario 4). You control what triggers a restart:

  • --watch — add a file or directory (repeat the flag for multiple roots). If you omit --watch, the CLI watches the current directory (.). Path-only: no glob patterns on --watch. Paths must exist when you start watch.

  • --watch-ignore — exclude paths or simple filename globs (repeatable). Path-shaped patterns like data/*.db are rejected — ignore the directory instead (--watch-ignore data/). SQLite database files are ignored by default (*.sqlite3, *.db, …).

Examples:

bash
stario watch main:bootstrap --watch app/
stario watch main:bootstrap --watch main.py
stario watch main:bootstrap --watch-ignore 'data/'   # directory must exist
stario watch main:bootstrap --watch-ignore '*.log'

Each reload is a full re-import with a fresh read of STARIO_* env vars. If the server fails to start (syntax or import error), fix the file and save again — watch does not retry until the next change. watchfiles debounces changes by about 1.6s.

Example: dev resync after reload

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, handle the next client connection by streaming a full HTML document over SSE.

You need all of the following before the snippet makes sense:

  • A route that holds an SSE response open—usually async with c.alive(): or async for _ in c.alive(source): around your send loop (Context.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).

  • full_page in the snippet is that document: bytes, str, or HTML nodes for <html>…</html>.

python
from stario.datastar import SSE
 
# full_page: bytes/str or html nodes for the whole document (<html>…</html>).
sse = SSE(w)
sse.patch_elements(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 — Prefer ordinary page loads or targeted patches for shipped behavior.