useSessionStorage


Custom hook that uses the sessionStorage API to persist state across page reloads.

Usage

import { useSessionStorage } from 'usehooks-ts'  export default function Component() {  const [value, setValue, removeValue] = useSessionStorage('test-key', 0)   return (  <div>  <p>Count: {value}</p>  <button  onClick={() => {  setValue((x: number) => x + 1)  }}  >  Increment  </button>  <button  onClick={() => {  setValue((x: number) => x - 1)  }}  >  Decrement  </button>  <button  onClick={() => {  removeValue()  }}  >  Reset  </button>  </div>  ) } 

API

useSessionStorage<T>(key, initialValue, options?): [T, Dispatch<SetStateAction<T>>, () => void]

Custom hook that uses the sessionStorage API to persist state across page reloads.

Type parameters

NameDescription
TThe type of the state to be stored in session storage.

Parameters

NameTypeDescription
keystringThe key under which the value will be stored in session storage.
initialValueT | () => TThe initial value of the state or a function that returns the initial value.
options?UseSessionStorageOptions<T>Options for customizing the behavior of serialization and deserialization (optional).

Returns

[T, Dispatch<SetStateAction<T>>, () => void]

A tuple containing the stored value, a function to set the value and a function to remove the key from storage.

Type aliases

Ƭ UseSessionStorageOptions<T>: Object

Represents the options for customizing the behavior of serialization and deserialization.

Type parameters

NameDescription
TThe type of the state to be stored in session storage.

Type declaration

NameTypeDescription
deserializer?(value: string) => TA function to deserialize the stored value.
initializeWithValue?booleanIf true (default), the hook will initialize reading the session storage. In SSR, you should set it to false, returning the initial value initially. Default ts true
serializer?(value: T) => stringA function to serialize the value before storing it.

Hook

import { useCallback, useEffect, useState } from 'react'  import type { Dispatch, SetStateAction } from 'react'  import { useEventCallback, useEventListener } from 'usehooks-ts'  declare global {  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions  interface WindowEventMap {  'session-storage': CustomEvent  } }  type UseSessionStorageOptions<T> = {  serializer?: (value: T) => string  deserializer?: (value: string) => T  initializeWithValue?: boolean }  const IS_SERVER = typeof window === 'undefined'  export function useSessionStorage<T>(  key: string,  initialValue: T | (() => T),  options: UseSessionStorageOptions<T> = {}, ): [T, Dispatch<SetStateAction<T>>, () => void] {  const { initializeWithValue = true } = options   const serializer = useCallback<(value: T) => string>(  value => {  if (options.serializer) {  return options.serializer(value)  }   return JSON.stringify(value)  },  [options],  )   const deserializer = useCallback<(value: string) => T>(  value => {  if (options.deserializer) {  return options.deserializer(value)  }  // Support 'undefined' as a value  if (value === 'undefined') {  return undefined as unknown as T  }   const defaultValue =  initialValue instanceof Function ? initialValue() : initialValue   let parsed: unknown  try {  parsed = JSON.parse(value)  } catch (error) {  console.error('Error parsing JSON:', error)  return defaultValue // Return initialValue if parsing fails  }   return parsed as T  },  [options, initialValue],  )   // Get from session storage then  // parse stored json or return initialValue  const readValue = useCallback((): T => {  const initialValueToUse =  initialValue instanceof Function ? initialValue() : initialValue   // Prevent build error "window is undefined" but keep working  if (IS_SERVER) {  return initialValueToUse  }   try {  const raw = window.sessionStorage.getItem(key)  return raw ? deserializer(raw) : initialValueToUse  } catch (error) {  console.warn(`Error reading sessionStorage key “${key}”:`, error)  return initialValueToUse  }  }, [initialValue, key, deserializer])   const [storedValue, setStoredValue] = useState(() => {  if (initializeWithValue) {  return readValue()  }   return initialValue instanceof Function ? initialValue() : initialValue  })   // Return a wrapped version of useState's setter function that ...  // ... persists the new value to sessionStorage.  const setValue: Dispatch<SetStateAction<T>> = useEventCallback(value => {  // Prevent build error "window is undefined" but keeps working  if (IS_SERVER) {  console.warn(  `Tried setting sessionStorage key “${key}” even though environment is not a client`,  )  }   try {  // Allow value to be a function so we have the same API as useState  const newValue = value instanceof Function ? value(readValue()) : value   // Save to session storage  window.sessionStorage.setItem(key, serializer(newValue))   // Save state  setStoredValue(newValue)   // We dispatch a custom event so every similar useSessionStorage hook is notified  window.dispatchEvent(new StorageEvent('session-storage', { key }))  } catch (error) {  console.warn(`Error setting sessionStorage key “${key}”:`, error)  }  })   const removeValue = useEventCallback(() => {  // Prevent build error "window is undefined" but keeps working  if (IS_SERVER) {  console.warn(  `Tried removing sessionStorage key “${key}” even though environment is not a client`,  )  }   const defaultValue =  initialValue instanceof Function ? initialValue() : initialValue   // Remove the key from session storage  window.sessionStorage.removeItem(key)   // Save state with default value  setStoredValue(defaultValue)   // We dispatch a custom event so every similar useSessionStorage hook is notified  window.dispatchEvent(new StorageEvent('session-storage', { key }))  })   useEffect(() => {  setStoredValue(readValue())  // eslint-disable-next-line react-hooks/exhaustive-deps  }, [key])   const handleStorageChange = useCallback(  (event: StorageEvent | CustomEvent) => {  if ((event as StorageEvent).key && (event as StorageEvent).key !== key) {  return  }  setStoredValue(readValue())  },  [key, readValue],  )   // this only works for other documents, not the current one  useEventListener('storage', handleStorageChange)   // this is a custom event, triggered in writeValueToSessionStorage  // See: useSessionStorage()  useEventListener('session-storage', handleStorageChange)   return [storedValue, setValue, removeValue] }