useStateHistorystate

useStateHistory

NewSSR SAFE

State management with undo, redo, and time-travel capabilities through a chronological history of state snapshots.

$ pnpm dlx shadcn@latest add @byphongphan/use-state-history
Requires @byphongphan in components.json
state
Loading preview...

Installation

$ pnpm dlx shadcn@latest add @byphongphan/use-state-history
Requires @byphongphan in components.json

Usage

Basic Undo and Redo

import { useStateHistory } from "@/shared/hooks"
export function TextEditor() { const { state: text, set: setText, undo, redo, canUndo, canRedo } = useStateHistory("Hello world")
return ( <div className="flex flex-col gap-3"> <div className="flex gap-2"> <button onClick={undo} disabled={!canUndo}>Undo</button> <button onClick={redo} disabled={!canRedo}>Redo</button> </div>
<textarea value={text} onChange={(e) => setText(e.target.value)} rows={4} /> </div> )}

Bounded History with Capacity Limit

import { useStateHistory } from "@/shared/hooks"
export function Counter() { // Keeps at most the latest 10 snapshots in memory const { state: count, set: setCount, undo, redo, history, pointer, jump } = useStateHistory(0, { capacity: 10 })
return ( <div> <p>Current Count: {count}</p> <button onClick={() => setCount((c) => c + 1)}>+1</button> <button onClick={undo}>Undo</button> <button onClick={redo}>Redo</button>
<div> <span>Snapshots: </span> {history.map((val, idx) => ( <button key={idx} onClick={() => jump(idx)} className={idx === pointer ? "font-bold text-primary" : ""} > {val} </button> ))} </div> </div> )}

Features

  • Undo & Redo Operations: Navigate backwards and forwards through previous state revisions with undo() and redo().
  • Automatic Timeline Branching: Setting a new state while viewing an older snapshot automatically prunes obsolete future history, preserving consistent timeline integrity.
  • Capacity Constraint: Optional capacity option prevents memory consumption by dropping the oldest entries once the limit is reached.
  • Direct Time-Travel: jump(index) allows jumping directly to any arbitrary snapshot with safe index boundary clamping.
  • Functional State Updates: set accepts either raw values or updater functions (prev) => nextVal, matching standard React setState ergonomics.
  • Lazy Initial State: Supports factory functions () => initialState for expensive initial state calculations.

Parameters

ParameterTypeDefaultDescription
initialStateT | (() => T)—The initial value or lazy initializer function.
options.capacitynumber | undefined—Maximum number of snapshots to retain. Oldest entries are purged.

Returns

Returns a UseStateHistoryReturn<T> object:

PropertyTypeDescription
stateTThe active state snapshot.
set(value: T | ((prev: T) => T)) => voidPushes a new state snapshot into the timeline.
undo() => voidReverts to the previous snapshot.
redo() => voidAdvances to the next snapshot.
canUndobooleanWhether backward navigation is possible (pointer > 0).
canRedobooleanWhether forward navigation is possible.
historyT[]Chronological array of all retained state snapshots.
pointernumberCurrent zero-based index within the history array.
jump(index: number) => voidDirectly moves the active pointer to a specific snapshot.
reset(newInitialState?: T) => voidResets the timeline with a new or initial baseline value.

Command Palette

Quick search and commands navigation