useExtensionStorage
Read and write the extension's shared key-value storage. Changes are persisted to the database and broadcast to all other surfaces in real time via WebSocket.
Signature
const [value, setValue] = useExtensionStorage<T>(key, initialValue);
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | Storage key. Not empty, at most 256 bytes (UTF-8), no : |
initialValue | T | Value returned until the stored value has loaded, and while the key was never stored |
Call the hook once per key. Each key is stored and synced on its own.
Return value
Returns a tuple of two values:
| Index | Type | Description |
|---|---|---|
0 | T | Current value of key (initialValue until a stored value arrives) |
1 | (value: T) => void | Setter - writes the new value of key; other keys are not touched |
Example
import { Lumio, CompactView, Toggle, TextField, useExtensionStorage } from "@zaflun/lumio-sdk";
function Settings() {
const [visible, setVisible] = useExtensionStorage("visible", true);
const [homeTeam, setHomeTeam] = useExtensionStorage("homeTeam", "");
return (
<CompactView title="Scoreboard">
<Toggle label="Show overlay" checked={visible} onChange={setVisible} />
<TextField label="Home team" value={homeTeam} onChange={setHomeTeam} />
</CompactView>
);
}
Lumio.render(<Settings />, { target: "editor" });
Reading in the layer
The same keys are readable in the layer surface:
import { Lumio, Box, Text, useExtensionStorage } from "@zaflun/lumio-sdk";
function Overlay() {
const [visible] = useExtensionStorage("visible", true);
const [homeTeam] = useExtensionStorage("homeTeam", "");
const [awayTeam] = useExtensionStorage("awayTeam", "");
const [homeScore] = useExtensionStorage("homeScore", 0);
const [awayScore] = useExtensionStorage("awayScore", 0);
if (!visible) return null;
return (
<Box style={{ position: "absolute", top: 20, right: 20 }}>
<Text content={`${homeTeam} ${homeScore} - ${awayScore} ${awayTeam}`} />
</Box>
);
}
Lumio.render(<Overlay />, { target: "layer" });
Grouping values under one key
Values that always change together can share one key. The setter then replaces that key's whole value, so spread the current one:
interface Score {
home: number;
away: number;
}
const [score, setScore] = useExtensionStorage<Score>("score", { home: 0, away: 0 });
// Correct: keeps `away`
setScore({ ...score, home: score.home + 1 });
// Wrong: drops `away` from the stored value
setScore({ home: 1 } as Score);
Keep independent settings on separate keys: every key is synced on its own, so two writes to different keys never overwrite each other.
Real-time sync
All surfaces receive storage updates instantly via WebSocket - there is no polling. When the editor calls a setter, the layer re-renders within milliseconds. Layers read storage but do not write it - see Shared Storage.
Persistence
Storage is persisted to PostgreSQL. It survives:
- Browser Source reloads
- Editor panel reloads
- Extension updates (the data schema is not versioned - ensure backward compatibility when changing field names)
Size limit
One value must serialize to at most 64 KiB of JSON; an installation holds at most 1 000 keys and 1 MiB in total (see Shared Storage). For larger structured data, use server functions.
Notes
Tis inferred frominitialValue; pass it explicitly for object values- Until the stored value has loaded, and for a key that was never stored, the hook returns
initialValue - The setter applies an optimistic update locally before confirming with the server
- Storage is scoped per installation - different overlay installs of the same extension have independent storage