Build Your First Stario App

In this tutorial, you'll build a real-time counter that demonstrates Stario's core: handlers, the Writer, and Datastar integration.

Step 1: Initialize Your Project

The fastest way is using the CLI:

pip install stario
stario init myapp --template hello-world
cd myapp

This sets up a project with uv and includes the Datastar client library.

Step 2: The Handler Pattern

Every Stario handler has the same signature:

async def handler(c: Context, w: Writer) -> None:
    pass
  • Context (c): Read request data (query, body, signals).
  • Writer (w): Send responses (HTML, JSON, SSE patches).

Step 3: Build the Counter

Update main.py with this reactive counter:

from stario import Context, Writer, Stario
from stario.html import Div, Button, H1
from stario.datastar import data, at

async def home(c: Context, w: Writer) -> None:
    w.html(
        Div({"id": "app"},
            data.signals({"count": 0}),
            H1("Counter"),
            Div(data.text("$count")),
            Button(data.on("click", at.post("/inc")), "+1"),
        )
    )

async def increment(c: Context, w: Writer) -> None:
    signals = await c.signals()
    w.sync({"count": signals.get("count", 0) + 1})

import asyncio

app = Stario()
app.get("/", home)
app.post("/inc", increment)

if __name__ == "__main__":
    asyncio.run(app.serve())

What's happening?

  1. data.signals initializes reactive state on the client.
  2. data.text binds the display to the $count signal.
  3. at.post("/inc") sends the current signals to the server.
  4. c.signals() reads those signals on the server.
  5. w.sync() updates the signals back on the client via SSE.

Next Steps