Skip to main content

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

key
string
required
The storage key to use for persisting the state.
defaultValue
TValue
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
boolean
default:"true"
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.
options.logger
(error: Error) => void
Custom error logger for storage operations.

Return Value

Returns a tuple:
state
TSlice
The current state value (or selected slice).
actions
StorageStoreApi<TValue>
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.
baseOptions.key
string
required
The storage key.
baseOptions.initialValue
TState
required
The initial/default value.
baseOptions.storageArea
Storage
The storage area to use.
baseOptions.syncStateAcrossTabs
boolean
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

Build docs developers (and LLMs) love