Zundo: Undo/Redo Middleware for Zustand

2.3.0 · active · verified Wed Apr 22

Zundo is a lightweight (under 700 bytes) undo/redo middleware designed for Zustand, enabling robust time-travel capabilities in JavaScript and TypeScript applications. Currently at version 2.3.0, it is actively maintained and frequently updated to ensure compatibility with new Zustand versions, including both v4 and v5. Zundo differentiates itself through its minimal bundle size, high flexibility offered by various optional middleware configurations for performance optimization, and an unopinionated, extensible API. It integrates seamlessly with existing Zustand projects, working effectively with multiple stores within a single application to provide undo/redo functionality without significant overhead. Zundo facilitates managing complex state changes by providing simple yet powerful history management.

Common errors

Warnings

Install

Imports

Quickstart

This example demonstrates how to create a Zustand store with `temporal` middleware, access its undo/redo capabilities, and reactively display historical state counts in a React component.

import { create } from 'zustand';
import { temporal } from 'zundo';
import { useStoreWithEqualityFn } from 'zustand/traditional';
import type { TemporalState } from 'zundo';

interface StoreState {
  bears: number;
  increasePopulation: () => void;
  removeAllBears: () => void;
}

const useStoreWithUndo = create<StoreState>()(
  temporal((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    removeAllBears: () => set({ bears: 0 }),
  })),
);

// To access temporal functions and reactive temporal state
interface MyTemporalState extends StoreState {
  temporal: TemporalState<StoreState>;
}
const useTemporalStore = () => useStoreWithEqualityFn(useStoreWithUndo.temporal.getState);

const App = () => {
  const { bears, increasePopulation, removeAllBears } = useStoreWithUndo();
  const { undo, redo, clear, pastStates, futureStates } = useTemporalStore();

  return (
    <>
      <h1>Bears: {bears}</h1>
      <button onClick={increasePopulation}>Increase</button>
      <button onClick={removeAllBears}>Remove All</button>
      <button onClick={() => undo()} disabled={pastStates.length === 0}>Undo</button>
      <button onClick={() => redo()} disabled={futureStates.length === 0}>Redo</button>
      <button onClick={() => clear()}>Clear History</button>
      <p>Past states count: {pastStates.length}</p>
      <p>Future states count: {futureStates.length}</p>
    </>
  );
};

export default App;

view raw JSON →