Hello world
Your first Stario app: one route, one handler, one HTML page. Everything lives in a single main.py. The goal is to see how a Stario process starts and how a request becomes a document — before Datastar, Relay, or a project tree.
Stario is a Python 3.14+ framework for hypermedia web apps. It is not ASGI. You run a bootstrap callable with stario serve or stario watch.
For where symbols live (stario vs stario.http), see the Glossary.
Prerequisites
You need Python 3.14+ and a project tool. These docs use uv; install Python 3.14 with uv python install 3.14 if needed.
1. Run the example
Clone the stario repo (or copy examples/hello-world into your workspace):
git clone https://github.com/bobowski/stario.gitcd stario/examples/hello-worlduv syncuv run stario watch main:bootstrapOpen http://127.0.0.1:8000 (set STARIO_PORT if the default is busy). For a one-shot run: uv run stario serve main:bootstrap.
You should see a heading and one sentence. That is the whole app.
2. The route
A Route is one HTTP method on one path. You declare it once, register it, and build links from the same object. There is no url_for.
from stario import Route HOME = Route("GET /")When one path has more than one method, compose a string and pin each Route. Realtime tiles and Go-to architecture show that split.
3. The view and the handler
A view is a pure function that returns an HTML tree. Tags live in stario.markup.html.
from stario.markup import html as h def home_view(): return h.HtmlDocument( {"lang": "en"}, h.Head(h.Title("Hello, Stario")), h.Body( h.H1("Hello, Stario"), h.P("This page is one handler and one route."), ), )Every handler has the same shape: async def handler(c: Context, w: Writer) -> None. The handler does not return a response object. It writes through Writer.
import stario.responses as responsesfrom stario import Context, Writer async def home(c: Context, w: Writer) -> None: responses.html(w, home_view())c is the request. w is the response. responses.html sets status, content type, and body, then finishes the writer. See Handlers and the writer.
4. Bootstrap
bootstrap is the composition root. It is an async generator with exactly one yield. Work before yield is startup. Work after yield is teardown.
from stario import App, Span async def bootstrap(app: App, span: Span): span.attr("app.name", "hello-world") app.add(HOME, home) yieldapp.add takes a Route and a handler. span.attr shows up in startup telemetry (TTY, JSON, or SQLite) before any request is handled. See Telemetry.
This example has no static files and no Datastar. That is intentional.
5. Scaffold a real project
Single-file examples teach mechanics. New repos should scaffold app/features/ and app/common/ from day one — see AI-assisted development and Structuring apps. Copy AGENTS.md into the repo root for agent sessions.
uv init --app my-appcd my-appuv add "stario>=4,<5"curl -o AGENTS.md https://stario.dev/AGENTS.mduv run stario watch app.main:bootstrapRead next
Realtime tiles — shared board: Relay, subscribe stream, POST commands
Datastar SDK — attributes, actions, and SSE from Python
Chat room — multi-file layout with SQLite and tests
The Stario way — the overarching idea