Signals vs. Patching

In Stario + Datastar, you have two ways to update the UI from the server.

1. w.sync (Update State)

Use w.sync when you want to update reactive data on the client. Datastar will automatically update any elements bound to those signals (via data.text, data.bind, etc.).

async def increment(c: Context, w: Writer):
    signals = await c.signals()
    w.sync({"count": signals["count"] + 1})
  • Pros: Cleanest logic, minimal data transfer.
  • Cons: Requires elements to be pre-bound to signals.

2. w.patch (Update UI)

Use w.patch when you want to replace a specific HTML fragment. Datastar finds the element by its id and morphs/replaces it.

async def update_msg(c: Context, w: Writer):
    w.patch(Div({"id": "status"}, "Operation Complete!"))
  • Pros: Can update any part of the page dynamically.
  • Cons: Transfers more data (HTML instead of just values).

Which one to choose?

Scenario Recommendation
Updating a counter or small value w.sync
Adding a new item to a list w.patch (with mode="append")
Replacing a complex component w.patch
Clearing a form after submission w.sync

Combining Both

You can use both in a single handler. Stario batches them into a single SSE stream response.

async def add_todo(c: Context, w: Writer):
    signals = await c.signals()

    # 1. Patch the list with the new item
    w.patch(Li(signals["new_todo"]), selector="#list", mode="append")

    # 2. Sync the input signal back to empty
    w.sync({"new_todo": ""})