useSessionStorage is useState that survives a reload but not a tab close. It works exactly like useState — const [value, setValue] = useSessionStorage(key, initial) — but the value is persisted to sessionStorage, which is scoped to a single tab and wiped when that tab closes. It's the right fit for a multi-step form's progress, a scroll position, or a "seen this modal" flag that shouldn't leak into other tabs the way localStorage does.
Implement useSessionStorage(key, initialValue). On first render, read the stored value (or fall back to initialValue). setValue updates React state and writes to sessionStorage, accepting a value or an updater function. If sessionStorage is unavailable (SSR, private mode, quota), degrade gracefully to plain useState behavior instead of throwing.
function useSessionStorage(key, initialValue) {
// returns [value, setValue] — like useState, backed by sessionStorage.
}
const [step, setStep] = useSessionStorage('checkout-step', 1);
setStep(2); // state updates AND sessionStorage['checkout-step'] = '2'
// reload the tab -> step is 2; close the tab -> gone
const [draft, setDraft] = useSessionStorage('draft', '');
setDraft((d) => d + '!'); // functional updater, like useState
sessionStorage is read once on mount, not on every render. Parse the stored JSON; fall back to initialValue if the key is absent or the JSON is corrupt.setValue must update state and JSON.stringify the value into sessionStorage.setValue(prev => next) like useState.localStorage, sessionStorage is per-tab, so there's no meaningful cross-tab storage event to subscribe to. That's the one piece useLocalStorage has that this hook doesn't need.window is undefined) and fall back gracefully; a failed write must not crash the component.