# useLocalStorage (/docs/hooks/use-local-storage)





`useLocalStorage` persists a piece of state to `localStorage`. Every instance using the same `key` stays in sync: a write in one component is reflected in every other instance immediately (no `CustomEvent` needed — a shared in-module listener registry handles same-tab sync), and across tabs via the native `storage` event.

```tsx
import { useLocalStorage } from '@42-central/ui-react';

const [columnSizing, setColumnSizing] = useLocalStorage({
  key: 'users-table',
  defaultValue: {},
});
```

It's SSR-safe: the hydration snapshot is always `defaultValue`, so there's no server/client mismatch to guard against — the real stored value is read in on the client shortly after mount.

## Usage [#usage]

Two independent hook instances sharing one key. Clicking either button updates both, since they're reading the same `localStorage` key.

<Preview code="&#x22;function Instance({ label }) {\n  const [count, setCount] = useLocalStorage({ key: 'demo', defaultValue: 0 });\n\n  return (\n    <div>\n      <span>{label}</span>\n      <span>{count}</span>\n      <Button onClick={() => setCount((c) => c + 1)}>Increment</Button>\n    </div>\n  );\n}&#x22;">
  <LocalStorageDemo />
</Preview>

If a write can't reach `localStorage` (quota exceeded, Safari private browsing), the value is kept in memory for the rest of the session instead of being lost — persistence is best-effort, but the UI never drops the update.

## API [#api]

### Parameters [#parameters]

<TypeTable
  type="{
  key: {
    type: 'string',
    required: true,
    description:
      'The localStorage key backing this value. Every hook instance sharing this key reads/writes the same value and stays in sync.',
  },
  defaultValue: {
    type: 'T',
    required: true,
    description:
      'Returned before the real stored value is read, whenever nothing is stored, or the stored value fails to deserialize. Captured once on mount, like a lazy useState initializer.',
  },
  serialize: {
    type: '(value: T) => string',
    default: 'JSON.stringify',
    description: 'Converts the value to a string before writing it to storage.',
  },
  deserialize: {
    type: '(raw: string) => T',
    default: 'JSON.parse',
    description: 'Parses the stored string back into a value.',
  },
}"
/>

### Returns [#returns]

A `[value, setValue, removeValue]` tuple.

<TypeTable
  type="{
  value: {
    type: 'T',
    required: true,
    description: 'The current value — defaultValue until the real stored value is read.',
  },
  setValue: {
    type: '(value: T | ((previous: T) => T)) => void',
    required: true,
    description: 'Accepts a value or an updater resolved against the current value.',
  },
  removeValue: {
    type: '() => void',
    required: true,
    description:
      'Deletes the key. Every instance on this key falls back to defaultValue until something is stored again.',
  },
}"
/>
