Bind browser storage to components
Define schema-validated storage keys and bind local or session storage reactively to Preact and React components.
Storage is persisted client state. For in-memory coordination across Preact island roots on one page, use stores instead.
Define keys once
Use the framework-owned Zod export and colocate reusable keys with the feature that owns them:
import { createStorageKey } from "brix/storage";
import { z } from "brix/zod";
export const themeKey = createStorageKey("theme", z.enum(["light", "dark"]));
export const profileKey = createStorageKey(
"profile",
z.object({ displayName: z.string(), visits: z.number().int().nonnegative() }),
);
A StorageKey is the plain { key, schema } definition. Reuse it rather than repeating a string and
schema or spreading the definition. Reads, writes, and hooks also accept an inline (key, schema)
pair, but a schema is never optional. Removal accepts either a storage key or a string because no
value needs validation.
Choose stable, application-specific key names. Schema changes must tolerate data written by an older application version or deliberately treat it as missing.
Use reactive hooks
"use island";
import { useLocalStorage } from "brix/hooks";
import { themeKey } from "./storage-keys.ts";
export function ThemePicker() {
const theme = useLocalStorage(themeKey);
return (
<select
value={theme.value ?? "light"}
onChange={(event) => {
const value = event.currentTarget.value;
if (value === "light" || value === "dark") theme.set(value);
}}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}
useLocalStorage and useSessionStorage return:
value, the parsed schema output ornullwhen absent, invalid, or unavailable.set(value), a typed write that validates and updates reactive state after persistence succeeds.remove(), which removes the item and updates subscribers.
Hooks for the same storage object and key synchronize within the current document, including changes
made through Brix raw helpers. Local-storage hooks also consume browser storage events, so another
tab's write re-renders subscribers. Session storage is tab-scoped and does not synchronize across
tabs.
For an app-specific Storage implementation, use useStorage(customStorage, storageKey) from the
matching Preact or React binding.
Continue with use typed browser storage helpers.