# Live rendering & the diff codec

How a Rask app stays live after first paint, and how it ships the smallest
possible payload on every state change.

For what happens *before* that first paint — waiting for a page's async data, deciding whether the
page needs a live connection at all, and what status and cache headers the response carries — see
[Render modes](https://rask.sh/docs/guides/render-modes/). This document picks up once a session exists.

It expands the *Live runtime & diff codec* section of [`CLAUDE.md`](https://github.com/pal-tamas/rask/blob/main/CLAUDE.md) with code
grounding. File references point at `src/Rask.Core/Live/`, `src/Rask.Server/`, and
`src/Rask.Wasm/`.

## Big picture: one component model, two transports

You write components once. The render walk, the frame stream, and the diff codec
all live in `Rask.Core` and are shared verbatim. Only the *transport* — how a
re-render reaches the browser DOM — differs by host:

```
                         Component tree (shared Rask.Core)
                                     │
                       HtmlSerializer.Serialize(...)
                                     │
              ┌──────────────────────┴──────────────────────┐
              │ HTML string                  RenderFrame stream │
              │ (StringBuilder)              (FrameWriter, parallel) │
              └──────────────────────┬──────────────────────┘
                                     │
                          FrameDiffer.Diff → List<EditOp>
                                     │
            ┌────────────────────────┴────────────────────────┐
            │ Server                              WASM         │
            │ LiveSession over a WebSocket        WasmLiveSession over JSImport/JSExport │
            │ (src/Rask.Server/LiveSession.cs)    (src/Rask.Wasm/WasmLiveSession.cs)     │
            └────────────────────────┬────────────────────────┘
                                     │
                  rask.js / rask.wasm.js → applyDiff / morph the DOM
```

- **Server** (`Rask.Server`): the runtime `<script>` opens a WebSocket. Inbound
  event-handler messages are dispatched in `LiveSession`; each render produces a
  payload that's pushed back down the socket.
- **WASM** (`Rask.Wasm`): there is no socket. `WasmLiveSession` is driven through
  `[JSImport]`/`[JSExport]` boundaries declared in `JSInterop.cs`. JS calls
  `[JSExport] DispatchAsync(byte[] json)` to deliver an event; .NET pushes the
  resulting payload back with the `[JSImport("applyRender")] ApplyRender(byte[])`
  import (the JSExport source generator doesn't support `Task<byte[]>` returns, so
  the result is *pushed*, not returned — see the comment in `JSInterop.cs`).

### The runtime `<script>` is auto-injected

`HtmlSerializer` injects the runtime as the **last child of `<body>`** — you never
write `RaskRuntimeScript()`. On serializing the `</body>` close it resolves the
host-registered `IRaskRuntimeScript` from DI and serializes its tag inline
(`HtmlSerializer.cs`, the `tagName == "body"` branch):

```csharp
if (tagName == "body" && live?.Services?.GetService<IRaskRuntimeScript>() is { } runtime
                      && runtime.Render() is { } runtimeScript)
{
    Serialize(runtimeScript, sb);
}
```

It is emitted **inline on every render** (not via a post-process sentinel) so its
bytes are stable across renders — the diff codec therefore never emits ops for it.
On **WASM no `IRaskRuntimeScript` provider is registered** (the runtime boots from
the page shell / `main.js`), so nothing is injected there.

## On this page

- [The render walk & diff codec](https://rask.sh/docs/guides/live-rendering-codec/) — the parallel HTML+frame walk, the edit-op codec, keyed reconciliation.
- [Cache, head & dispatch](https://rask.sh/docs/guides/live-rendering-runtime/) — SessionRenderCache, head/query-nav, handler ordering, slow-connection.

## See also

- [`../diagnostics.md`](https://rask.sh/docs/guides/diagnostics/) — **RASK022** (warning) flags a keyless list
  item that would reconcile positionally; add a `.Key(…)` to get trusted keyed structural
  ops instead of a full-HTML morph.
- [`CLAUDE.md`](https://github.com/pal-tamas/rask/blob/main/CLAUDE.md) — *Primitives & rules* (the authoritative summary).
- [`../authentication.md`](https://rask.sh/docs/guides/authentication/) — the auth handshake that forces full
  HTML (the `auth` out-of-band instruction the diff path gates out).
