Realtime decision tree

Pick Stream, Suspense, EventStream, signal(), or OOB for live and streaming UI

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

One question at a time

Chirp has five mechanisms that move HTML after (or while) a page loads. Pick by when the update happens and how many targets it must hit in one round trip — not by habit.

Situation Reach for Transport
Initial render, slow sections, one HTTP trip, shell paints first Suspense Single chunked response + OOB swaps
Initial render, SEO-heavy sections, no shell-first step Stream Single chunked response
Post-load live region on one page (chart, book, tape) EventStream + sse_scope() Long-lived SSE on that page
Cross-page chrome, fan-out, or post-mutation push to many bindings signal() on /_chirp/live One shared SSE connection
Multi-target mutation response in the same round trip OOB / FormActionfragments Normal htmx POST response

Rule of thumb: initial render that streams →Suspense (or Streamwhen you want progressive first byte without a skeleton shell). Updates after the page is live →EventStream for page-local regions, signal()when the same value must update chrome on every page. Several DOM targets from one POST →OOB.

Transport × client shape (LLM and chunked answers)

TemplateStreamalways renders a whole template file over chunked HTTP. It is not a fragment return type. Pair transport with the client wiring:

Full-page client htmx swap client
Chunked HTTP (TemplateStream) plain<form method="post"> not supported — nests a document inside#target
SSE (EventStream) rare Fragment scaffold + parametric sse-connect

Chirp warns on the bad pairing via thetemplate_stream_client_shapecontract. See Streaming answers for the three safe recipes and Contract categories.

See also Return values for the full type reference and Streaming HTML & Suspense for template patterns.

Lucky Cat map

The flagship Lucky Cat example uses all five on purpose — use it as a worked map, not as the default for a first app.

Feature Route / trigger Mechanism Why this one
Portfolio dashboard GET /portfolio Suspense Shell + six deferred panels, one trip
Market detail live blocks GET /markets/{symbol}/stream EventStream Page-local chart/book/tape after load
Free-threading proof panel GET /ft/stream EventStream Same — one page, one live region
Topbar ticker, balance, bell /_chirp/live signal() Cross-page chrome; one connection
Markets lobby board /_chirp/live(derived cascade) signal() One snapshot → three regions in lockstep
Deposit / trade fill POST /deposit, POST /trade/order signal()emit + 204 Balance/bell update without response body
Watchlist star toggle POST /watchlist/toggle OOB Star + rail count (+ optional card delete)
Trade fill (positions table) POST /trade/order FormAction+ OOB fragments Form reset + table + toast same POST
Trending / Research grids GETwith htmx Page / Fragment Snapshot per swap — no live re-rank

Doctrine and footguns for the shell live in the example'sDESIGN.md§4 and §7.

Start simpler

Before combining all five in one app, learn each mechanism in isolation:

  1. Suspense dashboard — one deferred panel
  2. SSE — minimal post-load stream
  3. Contacts shell — boosted nav +_actions.py
  4. Lucky Cat — capstone that composes everything