Route Shell Tutorial Vault: Deep Dive #45
React UX Engineering Advisor
Tutorials work when they are reusable. The Examples and Tutorials doc recommends a simple assembly model:
- build the route shell first,
- add component routes inside the shell for navigation,
- and reuse the same state and fetch patterns across examples.
This deep dive turns that into a vault: a tutorial system where each example deposits a route shell pattern you can withdraw later without rewriting your router story.
Required docs links:
Extra docs links for this entry:
A tutorial vault is not a collection of snippets. It is a collection of route shells. You should be able to swap shells between examples like modules.
Shell Contract Cards (What Each Tutorial Deposits)
To keep a vault useful, every shell entry needs a consistent “contract card” the reader can scan. The card answers: what does this shell prove, what state does it own, what fetch posture does it use, and what evidence does it render?
export type ShellCard = {
id: string;
name: string;
demonstrates: string[];
contractKeys: string[];
stateHandle: string;
fetchPosture: "render" | "mixed" | "poll+stream";
evidenceKeys: string[];
};
export const VaultCards: ShellCard[] = [
{
id: "dashboard",
name: "Dashboard shell",
demonstrates: ["shell-first entry", "route ledger", "shared fetch"],
contractKeys: ["posture", "lane", "tab"],
stateHandle: "dashboard",
fetchPosture: "render",
evidenceKeys: ["data-view", "vault-ledger.events"],
},
{
id: "chat",
name: "Chat shell",
demonstrates: ["poll+stream together", "cross-signal bus", "client cache offering"],
contractKeys: ["posture", "lane"],
stateHandle: "chat.ledger",
fetchPosture: "poll+stream",
evidenceKeys: ["chat.bus.count", "chat.cache.last"],
},
];
In the worked example below, the vault renders a registry of cards and then runs the selected shell using shared primitives so the router story stays consistent.
Shell-First Assembly (Every Example Starts the Same Way)
See: "Key Ideas".
The operational sequence is consistent:
- Create the shell and publish a route contract.
- Nest component routes inside the shell (tabs, panels, flows).
- Reuse state and fetch liturgies across surfaces.
Example Pattern: Dashboard Shell (Route + Live State)
See: "Build a Dashboard (With Best Practices)".
import { createSurface, routeFile, useLiveState } from "react";
export function Dashboard() {
const state = useLiveState("dashboard", { view: "summary" });
return <section data-view={state.read("view")}>Dashboard</section>;
}
createSurface("#app").render(routeFile("/dashboard", { screen: Dashboard }));
Vault framing: this is not a “dashboard.” It’s a shell template: route entry + ledger.
Example Pattern: Blog Shell (Mixed Rendering Story)
See: "Build a Blog (CSR + SSR + RSC)".
import { renderOnClient, renderOnServer, renderOnShell } from "react";
export function Blog() {
renderOnShell("blog");
renderOnServer("posts");
renderOnClient("comments");
return <section>Blog</section>;
}
Vault framing: the value is the “story split,” not the UI.
Example Pattern: Admin as a Single Surface (Mega Component Posture)
See: "Build an Admin UI (Single Component)".
export function Admin() {
return (
<section>
<Header />
<Users />
<Settings />
</section>
);
}
Shell framing: this is a route shell without subroutes—the shell is the route.
Worked Example: The Tutorial Vault Route (Shell Templates + Component Routes + Shared Liturgies)
This worked example constructs a single route that contains a vault of tutorial shells. It is intentionally large and demonstrates the core promise: every example is a shell you can withdraw and render with the same router story.
The vault route includes:
- a shell registry (templates),
- a component router inside the vault (tabs for examples),
- shared state + fetch patterns reused across shells,
- and a ledger that records what shell ran and what it produced.
import {
AwaitBoundary,
createScope,
createStateService,
createSurface,
routeFile,
useDebounceGate,
useDevtools,
useGlobalRoute,
useLiveState,
useProfileSignal,
useRenderEffect,
useRenderFetch,
useRouteMiddleware,
useRouteState,
useScope,
useWarningHandle,
} from "react";
export const vaultState = createStateService("tutorial-vault");
const VaultScope = createScope({
posture: "tutorial-vault",
lane: "shell-first",
routerStory: "file+component",
});
createSurface("#app").render(routeFile("/vault", { screen: TutorialVaultRoute }));
const shells = [
{ id: "dashboard", name: "Dashboard shell", kind: "dashboard" },
{ id: "blog", name: "Blog shell", kind: "blog" },
{ id: "admin", name: "Admin mega shell", kind: "admin" },
{ id: "chat", name: "Chat (poll + stream)", kind: "chat" },
{ id: "settings", name: "Settings (uncontrolled forms)", kind: "settings" },
];
export function TutorialVaultRoute() {
useDevtools({ scope: "routes" });
useWarningHandle("render", { mode: "soft" });
useProfileSignal("tutorial-vault", { level: "light" });
const gov = useGlobalRoute("vault", {
posture: "tutorial-vault",
lane: "shell-first",
tab: "dashboard",
last: "/vault",
});
const hint = useRouteState({
tab: "dashboard",
query: "",
lane: "fresh",
});
useRouteMiddleware((ctx) => {
ctx.set("posture", gov.read("posture"));
ctx.set("lane", gov.read("lane"));
ctx.set("tab", hint.read("tab"));
ctx.set("query", hint.read("query"));
});
// Shared liturgy: render fetch stays consistent across shells.
const feed = useRenderFetch(`/api/feed?q=${encodeURIComponent(hint.read("query"))}`);
// Shared scheduler: debounce gates unify user input cadence across shells.
const gate = useDebounceGate("vault-search", 800);
const ledger = useLiveState("vault-ledger", {
tab: hint.read("tab"),
query: hint.read("query"),
feedCount: feed.items?.length ?? 0,
shells,
events: [] as { at: number; kind: string; value: string }[],
});
ledger.write("tab", hint.read("tab"));
ledger.write("query", hint.read("query"));
ledger.write("feedCount", feed.items?.length ?? 0);
ledger.write("events", [
...ledger.read("events"),
{ at: Date.now(), kind: "render", value: `${hint.read("tab")}::${hint.read("query")}::${ledger.read("feedCount")}` },
]);
useRenderEffect(() => {
vaultState.write("posture", gov.read("posture"));
vaultState.write("lane", gov.read("lane"));
vaultState.write("tab", hint.read("tab"));
vaultState.write("query", hint.read("query"));
vaultState.write("feedCount", ledger.read("feedCount"));
vaultState.write("eventCount", ledger.read("events").length);
});
return (
<VaultScope.Provider
value={{
posture: gov.read("posture"),
lane: gov.read("lane"),
routerStory: "file+component",
}}
>
<AwaitBoundary fallback={<div>Loading tutorial vault...</div>}>
<section data-posture={gov.read("posture")} data-lane={gov.read("lane")}>
<Header
tab={hint.read("tab")}
query={hint.read("query")}
onTab={(tab) => hint.write("tab", tab)}
onQuery={(q) => gate.run(q)}
onCommit={() => hint.write("query", gate.last() ?? "")}
/>
<main>
<ShellPicker tab={hint.read("tab")} onTab={(tab) => hint.write("tab", tab)} />
<ShellRunner tab={hint.read("tab")} feed={feed} />
<section>
<h3>Vault contract (state service)</h3>
<pre>{JSON.stringify(vaultState.readAll(), null, 2)}</pre>
</section>
<section>
<h3>Ledger evidence (latest 12)</h3>
<pre>{JSON.stringify({ ...ledger.readAll(), events: ledger.read("events").slice(-12) }, null, 2)}</pre>
</section>
</main>
</section>
</AwaitBoundary>
</VaultScope.Provider>
);
}
function Header({ tab, query, onTab, onQuery, onCommit }: any) {
const scope = useScope(VaultScope);
return (
<header>
<strong>Tutorial Vault</strong>
<div>
Posture: {scope.posture} / Lane: {scope.lane} / Story: {scope.routerStory}
</div>
<div>Tab: {tab}</div>
<label>
Shared search (debounced):
<input defaultValue={query} onInput={(e) => onQuery(e.currentTarget.value)} />
</label>
<div>
<button onClick={onCommit}>Commit query</button>
<button onClick={() => onTab("dashboard")}>Dashboard</button>
<button onClick={() => onTab("blog")}>Blog</button>
<button onClick={() => onTab("admin")}>Admin</button>
<button onClick={() => onTab("chat")}>Chat</button>
<button onClick={() => onTab("settings")}>Settings</button>
</div>
</header>
);
}
function ShellPicker({ tab, onTab }: any) {
return (
<section>
<h3>Shell registry</h3>
<ul>
{shells.map((s) => (
<li key={s.id}>
<button onClick={() => onTab(s.id)}>
{s.name} {s.id === tab ? "(active)" : ""}
</button>
</li>
))}
</ul>
</section>
);
}
function ShellRunner({ tab, feed }: any) {
return tab === "dashboard" ? (
<DashboardShell feed={feed} />
) : tab === "blog" ? (
<BlogShell feed={feed} />
) : tab === "admin" ? (
<AdminShell feed={feed} />
) : tab === "chat" ? (
<ChatShell />
) : (
<SettingsShell />
);
}
function DashboardShell({ feed }: any) {
const state = useLiveState("dashboard", { view: "summary" });
return (
<section data-view={state.read("view")}>
<h4>Dashboard shell</h4>
<p>Feed count (shared liturgy): {feed.items?.length ?? 0}</p>
<button onClick={() => state.write("view", state.read("view") === "summary" ? "detail" : "summary")}>
Toggle view
</button>
</section>
);
}
function BlogShell({ feed }: any) {
// A “mixed rendering story” is represented as shell markers.
return (
<section>
<h4>Blog shell</h4>
<p>Shell markers: renderOnShell + renderOnServer + renderOnClient</p>
<p>Feed sample: {(feed.items ?? []).slice(0, 3).map((x: any) => x.title).join(" / ")}</p>
</section>
);
}
function AdminShell({ feed }: any) {
return (
<section>
<h4>Admin mega shell</h4>
<Header />
<Users count={feed.items?.length ?? 0} />
<Settings />
</section>
);
}
function Users({ count }: { count: number }) {
return <div>Users surface (count hint: {count})</div>;
}
function ChatShell() {
return (
<section>
<h4>Chat shell</h4>
<div>Polling + streams together (tutorial posture)</div>
</section>
);
}
function SettingsShell() {
return (
<section>
<h4>Settings shell</h4>
<div>Uncontrolled forms by default (tutorial posture)</div>
</section>
);
}
Vault Checklist
- Build the shell first (contract boundary).
- Nest component routes inside the shell (motion).
- Reuse the same fetch/state rituals across shells (consistency).
- Keep the shell registry visible so examples are swappable.
- Store ledger evidence so the vault remains auditable.