Overview
useStorageState provides hooks for managing state that automatically syncs with browser storage (localStorage or sessionStorage). It includes support for cross-tab synchronization, custom serialization, type-safe selectors, and more.
Import
import { useStorageState, createUseStorageState } from "@zayne-labs/toolkit-react";
useStorageState
A hook for managing state synchronized with browser storage.
Signature
const useStorageState = <TValue, TSlice = TValue>(
key: string,
defaultValue?: TValue,
options?: UseStorageStateOptions<TValue> & { select?: SelectorFn<TValue, TSlice> }
) => [state: TSlice, actions: StorageStoreApi<TValue>]
Parameters
The storage key to use for persisting the state.
The default value to use if no value exists in storage.
options
UseStorageStateOptions<TValue>
Configuration options for storage behavior.options.storageArea
Storage
default:"localStorage"
The storage area to use (localStorage or sessionStorage).
options.syncStateAcrossTabs
Whether to sync state changes across browser tabs.
options.serializer
(value: TValue) => string
Custom serialization function. Defaults to JSON.stringify.
options.parser
(value: string) => TValue
Custom parsing function. Defaults to JSON.parse.
options.select
SelectorFn<TValue, TSlice>
Selector function to derive a slice of the state.
options.equalityFn
(a: TSlice, b: TSlice) => boolean
Custom equality function for determining when to re-render.
options.partialize
(state: TValue) => Partial<TValue>
Function to select which parts of the state to persist.
Custom error logger for storage operations.
Return Value
Returns a tuple:
The current state value (or selected slice).
Object containing methods to interact with the storage:
setState(value): Updates the state
getState(): Gets the current state
subscribe(callback): Subscribes to state changes
removeItem(): Removes the item from storage
Usage
Basic Usage
import { useStorageState } from "@zayne-labs/toolkit-react";
function ThemeToggle() {
const [theme, themeActions] = useStorageState("theme", "light");
return (
<button onClick={() => themeActions.setState(theme === "light" ? "dark" : "light")}>
Current theme: {theme}
</button>
);
}
With TypeScript
import { useStorageState } from "@zayne-labs/toolkit-react";
type UserPreferences = {
notifications: boolean;
theme: "light" | "dark";
fontSize: number;
};
function SettingsPanel() {
const [preferences, preferencesActions] = useStorageState<UserPreferences>(
"user-preferences",
{
notifications: true,
theme: "light",
fontSize: 16,
}
);
return (
<div>
<label>
<input
type="checkbox"
checked={preferences.notifications}
onChange={(e) =>
preferencesActions.setState({
...preferences,
notifications: e.target.checked,
})
}
/>
Enable notifications
</label>
<select
value={preferences.theme}
onChange={(e) =>
preferencesActions.setState({
...preferences,
theme: e.target.value as "light" | "dark",
})
}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
Using Selectors
import { useStorageState } from "@zayne-labs/toolkit-react";
type AppState = {
user: { name: string; email: string };
settings: { theme: string };
};
function UserProfile() {
// Only subscribe to user slice
const [user] = useStorageState<AppState, AppState["user"]>(
"app-state",
undefined,
{
select: (state) => state.user,
}
);
return (
<div>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
</div>
);
}
Session Storage
import { useStorageState } from "@zayne-labs/toolkit-react";
function FormData() {
const [formData, formActions] = useStorageState(
"temp-form-data",
{ email: "", message: "" },
{ storageArea: sessionStorage }
);
return (
<form>
<input
type="email"
value={formData.email}
onChange={(e) =>
formActions.setState({ ...formData, email: e.target.value })
}
/>
<textarea
value={formData.message}
onChange={(e) =>
formActions.setState({ ...formData, message: e.target.value })
}
/>
</form>
);
}
createUseStorageState
Creates a custom storage state hook with shared base options.
Signature
const createUseStorageState = <TState>(
baseOptions: StorageOptions<TState>
) => UseBoundStorageState<TState>
Parameters
baseOptions
StorageOptions<TState>
required
Base configuration options that will be shared across all instances.The initial/default value.
baseOptions.syncStateAcrossTabs
Whether to sync across tabs.
baseOptions.serializer
(value: TState) => string
Custom serializer.
baseOptions.parser
(value: string) => TState
Custom parser.
Return Value
useCustomStorageState
UseBoundStorageState<TState>
A custom hook with the base options pre-configured. Can be called with an optional selector.
The hook also exposes all storage API methods directly (setState, getState, etc.).
Usage
import { createUseStorageState } from "@zayne-labs/toolkit-react";
type AppSettings = {
theme: "light" | "dark";
language: string;
notifications: boolean;
};
// Create a custom hook with shared configuration
const useAppSettings = createUseStorageState<AppSettings>({
key: "app-settings",
initialValue: {
theme: "light",
language: "en",
notifications: true,
},
syncStateAcrossTabs: true,
});
// Use in multiple components
function ThemeSelector() {
const [theme] = useAppSettings((state) => state.theme);
return (
<button onClick={() => useAppSettings.setState({ theme: "dark" })}>
Current: {theme}
</button>
);
}
function NotificationToggle() {
const [notifications] = useAppSettings((state) => state.notifications);
return (
<input
type="checkbox"
checked={notifications}
onChange={(e) =>
useAppSettings.setState({ notifications: e.target.checked })
}
/>
);
}
Notes
- State is automatically synced across tabs by default (can be disabled with
syncStateAcrossTabs: false)
- All options are memoized using stable callback references to prevent unnecessary re-renders
- Storage errors are handled gracefully and can be logged with a custom logger
- Built on top of
createExternalStorageStore from @zayne-labs/toolkit-core
- Supports partial state persistence via the
partialize option
- Uses
useStore internally for efficient subscriptions