Keys, renderId and what must match
A key starts with the renderId, then one number for each enclosing component call, then the number of the template root, all joined with - (0-0-0-0 above is…
A key starts with the renderId, then one number for each enclosing component call, then the number of
the template root, all joined with - (0-0-0-0 above is renderId 0, the Page call, the App call,
the first template of App). Each component call opens a scope; components and template roots draw
their numbers from that scope's counter in the order they run. Keys therefore depend on the shape of the
tree and nothing else: not on owners, effects, timing or memos. Server and client agree as long as they
run the same components with the same structure.
renderId defaults to "0". Give each render a different id when more than one is on a page (several
independent apps, or fragments rendered by separate server calls), and pass the same value to
hydrate. It may contain letters, digits, _ and -; anything else throws a TypeError.
import { renderToString } from "@shadow-js/web/server";
import { Page } from "./src/app";
console.log(renderToString(() => <Page />, { renderId: "left" }));
<main data-hk=When the client disagrees with the server#
Hydration never leaves a half-hydrated tree. What the client does depends on the difference:
| Difference | Result |
|---|---|
| A text value differs | the server's text node is reused and its data is overwritten; development warns HYDRATION_TEXT_MISMATCH with the expected and the server text (a region the server left empty claims no text and is not compared) |
| The server sent more list items than the client wants | the extra elements are removed |
| The client wants an element the server did not send (no element for the key) | HYDRATION_KEY_MISS warning in development; the element is created on the client |
Wrong renderId, or no server markup at all |
every key misses; everything is created on the client and replaces the server markup |
Treat a HYDRATION_KEY_MISS as a bug in your app, not as a recoverable condition: it means the server
and client ran different code or different data. The usual causes are branching on window or
Date.now() during render, data that differs between the two (a request-specific value that the
client does not have), and a mismatching renderId.
A HYDRATION_TEXT_MISMATCH has the same causes for text: the client computed a different text for a
region than the server wrote. Its warning shows both texts, escaped and cut to 40 characters.