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 [storage, setStorage] = useExtensionStorage<T>();
Parameters
This hook takes no parameters.
Return value
Returns a tuple of two values:
| Index | Type | Description |
|---|---|---|
0 | T | Current storage object (defaults to {} on first load) |
1 | (storage: T) => void | Setter function — replaces the entire storage object |
Example
import { Lumio, CompactView, Toggle, TextField, useExtensionStorage } from "@zaflun/lumio-sdk";
interface MyStorage {
visible: boolean;
homeTeam: string;
awayTeam: string;
homeScore: number;
awayScore: number;
}
function Settings() {
const [storage, setStorage] = useExtensionStorage<MyStorage>();
// Safely read with defaults
const visible = storage.visible ?? true;
const homeTeam = storage.homeTeam ?? "";
return (
<CompactView title="Scoreboard">
<Toggle
label="Show overlay"
checked={visible}
onChange={(checked) => setStorage({ ...storage, visible: checked })}
/>
<TextField
label="Home team"
value={homeTeam}
onChange={(value) => setStorage({ ...storage, homeTeam: value })}
/>
</CompactView>
);
}
Lumio.render(<Settings />, { target: "editor" });
Reading in the layer
The same storage is accessible in the layer surface:
import { Lumio, Box, Text, useExtensionStorage } from "@zaflun/lumio-sdk";
function Overlay() {
const [storage] = useExtensionStorage<MyStorage>();
if (!storage.visible) return null;
return (
<Box style={{ position: "absolute", top: 20, right: 20 }}>
<Text content={`${storage.homeTeam} ${storage.homeScore ?? 0} - ${storage.awayScore ?? 0} ${storage.awayTeam}`} />
</Box>
);
}
Lumio.render(<Overlay />, { target: "layer" });
Spread rule
The setter replaces the entire storage object. Always spread the current value:
// Correct: preserves all other keys
setStorage({ ...storage, visible: true });
// Wrong: deletes all other keys!
setStorage({ visible: true });
Real-time sync
All surfaces receive storage updates instantly via WebSocket — there is no polling. When the editor calls setStorage(), the layer re-renders within milliseconds.
Persistence
Storage is persisted to PostgreSQL. It survives:
- OBS Browser Source reloads
- Page refreshes on the interactive surface
- Extension updates (the data schema is not versioned — ensure backward compatibility when changing field names)
Size limit
The storage object must serialize to under 64 KB of JSON. For larger structured data, use server functions.
Notes
- The type parameter
Tis optional but strongly recommended - On first load, storage is
{}— always use?? defaultValuewhen reading fields setStorage()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 objects