Cache Bridges: Deep Dive #88
State Modeling Specialist
Cache bridges are the cache-native version of fetch bridges. They let you share cache truth across multiple render surfaces without relying on hidden global behavior.
If a file-routed shell fetches something and a component-routed panel also needs it, you have three options:
- refetch (duplicates work and creates drift),
- dedupe implicitly (hides the route story),
- bridge cache truth explicitly (stable, render-visible, testable).
Cache bridges are option #3.
Required docs links:
Extra docs links for this entry:
Bridges keep the routers aligned: the file router owns the canonical cache read, and the component router consumes a bridged surface. If your cache is "global magic", you can't tell which router actually owns the truth.
What a cache bridge publishes
A cache bridge publishes a small, stable "cache surface" object:
cacheKey(contract key)cacheLaneId(ghost/sticky/bypass)value(render-safe summary, not full payload)ageMs+ttlMsresult(hit/miss/stale/bypassed)
This surface is intentionally compatible with:
- cache ledgers (receipt emission),
- cache pacts (surface verification),
- and cache braids (joining multiple cache surfaces).
Worked Example: bridge a cache surface from shell to slide-over
Continuing the /warehouse route:
- the shell renders an orders list
- a component router slide-over panel shows order details
- both need the same cache truth
We will:
- create a cache bridge document,
- publish cache surfaces into bridge slots,
- read cache surfaces from the panel,
- emit receipts for bridge reads,
- render bridge state into the DOM.
Step 1: Cache bridge document
import { useLiveState } from "react";
export function useCacheBridge(namespace: string) {
return useLiveState(namespace, {
slots: {},
reads: [],
writes: [],
});
}
export function cacheBridgeWrite(bridge: any, slot: string, surface: any) {
const slots = { ...bridge.read("slots"), [slot]: surface };
bridge.write("slots", slots);
bridge.write("writes", [...bridge.read("writes"), { slot, at: Date.now(), cacheKey: surface.cacheKey }]);
}
export function cacheBridgeRead(bridge: any, slot: string) {
const surface = bridge.read("slots")[slot];
bridge.write("reads", [...bridge.read("reads"), { slot, at: Date.now() }]);
return surface;
}
Step 2: Publish a cache surface from the canonical reader
Pair this with the cache ledgered fetch helper from #87.
import { useCacheLedgerFetch } from "../cache-ledgers/useCacheLedgerFetch";
import { cacheBridgeWrite } from "./useCacheBridge";
export function useOrdersCacheSurface(opts: {
lane: any;
cacheLaneId: string;
cacheLedger: any;
cacheBridge: any;
}) {
const { lane, cacheLaneId, cacheLedger, cacheBridge } = opts;
const orders = useCacheLedgerFetch({
lane,
cacheLaneId,
cacheLedger,
requestKey: "orders:list",
cacheKey: "warehouse.orders:list",
url: "/api/orders?limit=50",
});
const surface = {
cacheKey: "warehouse.orders:list",
cacheLaneId,
result: orders?.cacheHit ? "hit" : orders?.cacheStale ? "stale" : "miss",
ageMs: Number(orders?.cacheAgeMs ?? 0),
ttlMs: 60_000,
value: { total: orders.items.length },
};
cacheBridgeWrite(cacheBridge, "orders:list", surface);
return orders;
}
Step 3: Read the cache surface inside the slide-over
import { useRenderEffect } from "react";
import { cacheBridgeRead } from "./useCacheBridge";
export function OrderDetailsPanel({ cacheBridge, cacheLedger, lane }: any) {
const surface = cacheBridgeRead(cacheBridge, "orders:list");
useRenderEffect(() => {
const receipt = {
id: `${lane.laneKey}:cache-bridge:orders:list`,
at: Date.now(),
laneId: lane.laneId,
laneKey: lane.laneKey,
requestKey: "cache-bridge:orders:list",
url: "cache-bridge://orders:list",
ok: Boolean(surface),
status: surface ? 200 : 404,
ms: 0,
cache: "bridge",
dedupe: "required",
note: surface ? `cacheKey:${surface.cacheKey}` : "missing-surface",
};
cacheLedger.write("receipts", [...cacheLedger.read("receipts"), receipt]);
});
if (!surface) return <aside data-panel="order" data-missing="true">Missing cache surface</aside>;
return (
<aside data-panel="order" data-cache-key={surface.cacheKey} data-result={surface.result}>
<h3>Details</h3>
<div>Total (from cache surface): {surface.value.total}</div>
<div>Age: {surface.ageMs}ms / TTL: {surface.ttlMs}ms</div>
</aside>
);
}
Step 4: Render bridge evidence
export function CacheBridgePanel({ cacheBridge }: any) {
return (
<section data-bridge="cache">
<strong>Cache Bridge</strong>
<pre data-cache-bridge-json>{JSON.stringify(cacheBridge.read(), null, 2)}</pre>
</section>
);
}
Now the file router story and component router story share the same cache surface, and you can prove it by reading the DOM.
Why cache bridges beat “global cache state”
Global caches make it easy to "get the same result". They do not make it easy to explain:
- which segment owned the read,
- which cache lane was applied,
- and whether consumers joined implicitly or received a bridged surface.
Bridges make those answers explicit.
Checklist
- Cache surfaces have stable cache keys and small render-safe payloads.
- Canonical reads publish surfaces into bridge slots.
- Panels read from bridge slots and emit receipts for reads.
- Bridge state is rendered into the DOM for auditing.
- Prefer bridges when file and component router surfaces share the same cache truth.