Skip to main content

useLocalStorage

A Hook for managing localStorage, providing type-safe local storage operations with cross-tab synchronization support.

Basic Usage

Live Editor
function Demo() {
  const [value, setValue] = useLocalStorage({ key: 'demo-value', defaultValue: '' });

  return (
    <div>
      <Input
        placeholder="Enter content (auto-saved)"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <div style={{ marginTop: '12px', fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
        Currently stored value: {value || '(empty)'}
      </div>
      <Button
        onClick={() => setValue('')}
        size="small"
        style={{ marginTop: '12px' }}
      >
        Clear
      </Button>
    </div>
  );
}
Result
Loading...

Theme Persistence

Save user theme preference:

Live Editor
function Demo() {
  const [theme, setTheme] = useLocalStorage({
    key: 'app-theme',
    defaultValue: 'light'
  });

  return (
    <div>
      <Group spacing="md">
        <Button
          onClick={() => setTheme('light')}
          variant={theme === 'light' ? 'filled' : 'outline'}
        >
          ☀️ Light
        </Button>
        <Button
          onClick={() => setTheme('dark')}
          variant={theme === 'dark' ? 'filled' : 'outline'}
        >
          🌙 Dark
        </Button>
        <Button
          onClick={() => setTheme('auto')}
          variant={theme === 'auto' ? 'filled' : 'outline'}
        >
          🔄 Auto
        </Button>
      </Group>
      <div
        style={{
          marginTop: '20px',
          padding: '24px',
          backgroundColor:
            theme === 'dark' ? '#1a1a1a' :
            theme === 'light' ? '#ffffff' :
            'var(--ifm-background-surface-color)',
          color:
            theme === 'dark' ? '#ffffff' :
            theme === 'light' ? '#000000' :
            'var(--ifm-font-color-base)',
          border: '1px solid var(--ifm-color-emphasis-300)',
          borderRadius: '8px',
          transition: 'all 0.3s',
        }}
      >
        <h4>Current theme: {theme}</h4>
        <p>Theme persists after page refresh</p>
      </div>
    </div>
  );
}
Result
Loading...

User Preferences

Save complex user configuration:

Live Editor
function Demo() {
  const [fontSize, setFontSize] = useLocalStorage({
    key: 'font-size',
    defaultValue: '16'
  });
  const [notifications, setNotifications] = useLocalStorage({
    key: 'notifications',
    defaultValue: 'true'
  });

  return (
    <div>
      <div style={{ marginBottom: '20px' }}>
        <label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
          Font Size
        </label>
        <Group spacing="sm">
          {['14', '16', '18', '20'].map((size) => (
            <Button
              key={size}
              size="small"
              variant={fontSize === size ? 'filled' : 'outline'}
              onClick={() => setFontSize(size)}
            >
              {size}px
            </Button>
          ))}
        </Group>
      </div>

      <div style={{ marginBottom: '20px' }}>
        <label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
          <input
            type="checkbox"
            checked={notifications === 'true'}
            onChange={(e) => setNotifications(e.target.checked ? 'true' : 'false')}
            style={{ marginRight: '8px' }}
          />
          <span>Enable notifications</span>
        </label>
      </div>

      <div
        style={{
          padding: '20px',
          backgroundColor: 'var(--ifm-color-emphasis-100)',
          borderRadius: '8px',
          fontSize: `${fontSize}px`,
        }}
      >
        <h4 style={{ margin: '0 0 12px 0' }}>Preview Area</h4>
        <p style={{ margin: 0 }}>
          Font size: {fontSize}px {notifications === 'true' && '· Notifications enabled 🔔'}
        </p>
      </div>
    </div>
  );
}
Result
Loading...

Form Draft Auto-save

Automatically save form input:

Live Editor
function Demo() {
  const [title, setTitle] = useLocalStorage({ key: 'draft-title', defaultValue: '' });
  const [content, setContent] = useLocalStorage({ key: 'draft-content', defaultValue: '' });

  const handleClear = () => {
    setTitle('');
    setContent('');
  };

  return (
    <div>
      <div style={{ marginBottom: '12px' }}>
        <Input
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
      </div>
      <div style={{ marginBottom: '12px' }}>
        <Textarea
          placeholder="Content (auto-save draft)"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          rows={4}
        />
      </div>
      <Group spacing="md">
        <Button onClick={handleClear} variant="outline">
          Clear Draft
        </Button>
      </Group>
      {(title || content) && (
        <div
          style={{
            marginTop: '16px',
            padding: '12px',
            backgroundColor: 'var(--ifm-color-success-lightest)',
            borderRadius: '6px',
            fontSize: '14px',
          }}
        >
          ✓ Draft auto-saved locally
        </div>
      )}
    </div>
  );
}
Result
Loading...

Functional Updates

Supports updates based on current value:

Live Editor
function Demo() {
  const [count, setCount] = useLocalStorage({ key: 'counter', defaultValue: '0' });

  const increment = () => setCount((prev) => String(Number(prev) + 1));
  const decrement = () => setCount((prev) => String(Number(prev) - 1));
  const reset = () => setCount('0');

  return (
    <div style={{ textAlign: 'center' }}>
      <div
        style={{
          fontSize: '48px',
          fontWeight: 'bold',
          marginBottom: '20px',
          color: 'var(--ifm-color-primary)',
        }}
      >
        {count}
      </div>
      <Group spacing="md">
        <Button onClick={decrement}>-</Button>
        <Button onClick={increment}>+</Button>
        <Button onClick={reset} variant="outline">Reset</Button>
      </Group>
      <p style={{ marginTop: '16px', fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
        Count is saved to localStorage
      </p>
    </div>
  );
}
Result
Loading...

API

Parameters

function useLocalStorage<T extends string>({
key,
defaultValue,
}: {
key: string;
defaultValue?: T;
}): [T, (val: T | ((prevState: T) => T)) => void]
ParameterDescriptionTypeDefault
keylocalStorage key namestring-
defaultValueDefault valueTundefined

Return Value

Returns an array, similar to useState:

IndexDescriptionType
[0]Current valueT
[1]Update function(val: T | ((prevState: T) => T)) => void

Features

  • Type Safe: Provides TypeScript type support
  • Cross-tab Sync: Automatically syncs values across multiple tabs
  • SSR Friendly: Safely handles server-side rendering scenarios
  • Default Value: Supports setting default values
  • Functional Updates: Supports updates based on current value

Usage Scenarios

  • User Preferences: Save theme, language, and other user settings
  • Form Drafts: Automatically save form inputs
  • Shopping Cart: Persist shopping cart data
  • Browse History: Record user browsing history
  • Temporary Data: Store temporary data that doesn't need server sync