useSessionStorageState

State Management

A React hook that persists state in sessionStorage, maintaining data for the duration of a page session.

Demo

This counter persists across page refreshes but is cleared when closing the browser tab:

0

Installation

npm install @thibault.sh/hooks

Usage

import { useSessionStorageState } from '@thibault.sh/hooks/useSessionStorageState';

function FormWithAutosave() {
  const [formData, setFormData] = useSessionStorageState('form-draft', {
    title: '',
    content: ''
  });
  
  return (
    <form>
      <input
        value={formData.title}
        onChange={(e) => setFormData(prev => ({
          ...prev,
          title: e.target.value
        }))}
        placeholder="Title"
      />
      <textarea
        value={formData.content}
        onChange={(e) => setFormData(prev => ({
          ...prev,
          content: e.target.value
        }))}
        placeholder="Content"
      />
    </form>
  );
}

API

Parameters

  • key
    string

    The sessionStorage key

  • initialValue
    T

    The initial value to use if no value exists in storage

Returns

A tuple containing the current value and a setter function

Features

  • Session Persistence

    Data persists until the browser tab is closed

  • Page Refresh Safe

    State survives page refreshes

  • Type Safety

    Full TypeScript support with generics

  • Automatic Serialization

    JSON serialization handled automatically

Form Draft Example

import { useSessionStorageState } from '@thibault.sh/hooks';

interface FormDraft {
  title: string;
  content: string;
  lastSaved: string;
}

function BlogPostEditor() {
  const [draft, setDraft] = useSessionStorageState<FormDraft>(
    'blog-post-draft',
    {
      title: '',
      content: '',
      lastSaved: new Date().toISOString()
    }
  );

  const updateDraft = (updates: Partial<FormDraft>) => {
    setDraft(prev => ({
      ...prev,
      ...updates,
      lastSaved: new Date().toISOString()
    }));
  };

  return (
    <div>
      <h2>Draft Post</h2>
      <p>Last saved: {new Date(draft.lastSaved).toLocaleString()}</p>
      <input
        value={draft.title}
        onChange={(e) => updateDraft({ title: e.target.value })}
        placeholder="Post title"
      />
      <textarea
        value={draft.content}
        onChange={(e) => updateDraft({ content: e.target.value })}
        placeholder="Write your post..."
      />
    </div>
  );
}

Wizard Form Example

import { useSessionStorageState } from '@thibault.sh/hooks';

interface WizardState {
  currentStep: number;
  steps: {
    personalInfo: {
      name: string;
      email: string;
    };
    preferences: {
      newsletter: boolean;
      theme: string;
    };
  };
}

function FormWizard() {
  const [wizardState, setWizardState] = useSessionStorageState<WizardState>(
    'wizard-state',
    {
      currentStep: 1,
      steps: {
        personalInfo: { name: '', email: '' },
        preferences: { newsletter: false, theme: 'light' }
      }
    }
  );

  const nextStep = () => {
    setWizardState(prev => ({
      ...prev,
      currentStep: prev.currentStep + 1
    }));
  };

  return (
    <div>
      <h2>Step {wizardState.currentStep} of 2</h2>
      {wizardState.currentStep === 1 ? (
        <div>
          <input
            value={wizardState.steps.personalInfo.name}
            onChange={(e) => 
              setWizardState(prev => ({
                ...prev,
                steps: {
                  ...prev.steps,
                  personalInfo: {
                    ...prev.steps.personalInfo,
                    name: e.target.value
                  }
                }
              }))
            }
            placeholder="Name"
          />
          <button onClick={nextStep}>Next</button>
        </div>
      ) : (
        <div>
          <select
            value={wizardState.steps.preferences.theme}
            onChange={(e) =>
              setWizardState(prev => ({
                ...prev,
                steps: {
                  ...prev.steps,
                  preferences: {
                    ...prev.steps.preferences,
                    theme: e.target.value
                  }
                }
              }))
            }
          >
            <option value="light">Light</option>
            <option value="dark">Dark</option>
          </select>
        </div>
      )}
    </div>
  );
}