npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ledgex/react

v0.5.3

Published

Key-value state management with efficient undo/redo for React

Readme

React Ledgex

"Smart, memory-efficient state management for React apps with built-in undo/redo. Inspired by Git and Photoshop"

React Ledgex Logo

npm License: MIT


Table of Contents

Features

Simple Key-Value Store – Works like useState but globally shared and fully type-safe. Time Travel – Built-in undo/redo with minimal overhead. Layer Support – Isolate and manage state changes like Photoshop layers. Smart & Efficient Storage

  • Only stores the attributes that actually changed, not entire objects.
  • No duplicated entries: repeated actions that don’t change state are ignored.
  • This means even with large objects (e.g., 30+ attributes), memory usage stays minimal because unchanged properties reference existing state. No Empty Tickets – You only "pay" for meaningful changes. Setting a color to red twice won’t create two history entries. Tiny Bundle – Less than 5KB gzipped. React Optimized – Works seamlessly with hooks and integrates easily into existing apps.

Installation

npm install @ledgex/react
# or
yarn add @ledgex/react

Basic Usage

1. Wrap your app

import { LedgexProvider } from '@ledgex/react';

function App() {
  return (
    <LedgexProvider>
      <PhotoEditor />
    </LedgexProvider>
  );
}

2. Use anywhere

import { useLedgex } from '@ledgex/react';

function Editor() {
  const { set, get, undo, redo, getTimeInfo } = useLedgex();
  const { canUndo, canRedo } = getTimeInfo();

  const layer = get('background');

  const handleEdit = () => {
    set('background', { color: '#202020' });
  };

  return (
    <div style={{ background: layer?.color }}>
      <button onClick={undo}>Undo</button>
    </div>
  );
}

Real-World Example: Photo Editor Layer Backups (with Undo/Redo)

This example demonstrates how you can use Ledgex to back up complex UI states efficiently—perfect for use cases like photo editors, where layers are updated frequently and users expect smooth undo/redo functionality.

Concept

  • Use React state (useLayers) for live rendering and immediate feedback to the user.
  • Use Ledgex state (useLedgex) to store history snapshots for undo/redo actions.
  • Apply throttling to prevent storing too many intermediate states (e.g., when the user is dragging or rapidly changing properties).

Example Code

import { useMemo } from 'react';
import throttle from 'lodash/throttle';
import { useLayers } from './useLayers'; // your live state hook
import { useLedgex } from '@ledgex/react';

const { layers, setLayers } = useLayers(); // For live rendering
const { set, get, undo, redo } = useLedgex(); // For history management (undo/redo)

// Throttle backups to one every 300ms
const throttledSet = useMemo(() => throttle(set, 300), [set]);

// Update layer properties and back up state
const handlePropsChange = (layerId, newProps) => {
    setLayers(prevLayers =>
        prevLayers.map(layer => {
            if (layer.id === layerId) {
                const updatedLayer = layer.clone();
                updatedLayer.updateProps(newProps);

                // Backup the updated layer to Ledgex
                throttledSet({
                    [layerId]: AbstractLayer.toObj(updatedLayer) // Store as Object
                });

                return updatedLayer;
            }
            return layer;
        })
    );
};

Why This Works So Well

  • Efficient Memory Usage: Only essential snapshots are stored, avoiding memory bloat during rapid updates.

  • Smooth User Experience: Users get instant live previews while undo/redo history is safely maintained in Ledgex.

  • Throttle-Friendly: Frequent updates (e.g., dragging, key presses) won’t overwhelm your history—only meaningful snapshots are stored.


Advanced Features

Batch Updates (Atomic State Changes)

Ledgex allows you to batch multiple updates into a single undo/redo point. This is ideal for actions where multiple properties or objects should be treated as one meaningful change.

  • Example: Updating both color and position of a layer counts as one undo step.
  • Example: Aligning three layers at once → a single undo reverts all of them.
useLedgex.set({
  'layer1': { x: 100, y: 200 },
  'layer2': { x: 100, y: 200 },
  'layer3': { x: 100, y: 200 }
}); // Single undo/redo point

This enables:

  • Groups related actions → cleaner user experience.
  • Prevents clutter in history with unnecessary intermediate states.

Deeply Nested Object Support

Ledgex handles nested objects efficiently, updating only the deepest meaningful changes. You can safely update complex objects without worrying about memory blow-up or redundant history entries.

useLedgex.set({
  'layer1': {
    filters: {
      brightness: 1.2,
      contrast: 0.8
    }
  }
});

Why it matters:

  • Works seamlessly with structured data like layers, filters, and settings.
  • Stores only what actually changed, even deep inside the object hierarchy.

These advanced features make Ledgex powerful for apps like photo editors, diagram tools, or any software with complex state—without sacrificing performance or simplicity.

Efficient History Management

Ledgex gives you full control over how much history is stored by allowing you to configure a buffer size. This ensures you never use more memory than needed, even in apps with thousands of state changes.

// Limit history to the last 100 meaningful changes
<LedgexProvider bufferSize={100}>
  <YourApp />
</LedgexProvider>

How it works:

  • The bufferSize prop defines how many undo/redo states to keep in memory.
  • Older states are automatically discarded when the buffer is full.
  • Combined with smart diffing, only meaningful changes consume space.

Why This Matters

  • Memory Safety → Prevents history from growing indefinitely.
  • Performance → Keeps undo/redo operations fast, even in long editing sessions.
  • Flexibility → Developers can adjust buffer size based on app needs (e.g., 50 for light apps, 500 for professional editors).

Tip: Use a reasonable buffer size based on your use case to balance memory footprint and history depth.


Why Ledgex?

| Feature | Ledgex | Redux | Zustand | |-------------------|---------------------|------------------|----------------| | Undo/Redo | ✅ Built-in | ❌ Middleware | ❌ Manual | | Key-Value Store | ✅ Native | ❌ Actions | ✅ Native | | Layer Support | ✅ Photoshop-style | ❌ | ❌ | | Memory Efficiency | ✅ Diff-based & No Empty Tickets | ❌ Full snapshots | ❌ Full snapshots | | Bundle Size | 4~5KB | 16KB | 8KB |


Benchmarks

Benchmark: Low-Entropy Updates on a Large Object (30 attributes, Neglecting Setup overhead)

Scenario

  • Object has 30 attributes (~1 KB each, ~30 KB total).
  • You perform 1000 updates, but only 1 attribute changes per update.

| State Manager | Storage per update | Total Stored (1000 updates) | Efficiency | | ----------------------- | ------------------------- | ------------------------------- | ------------------- | | Ledgex (diff-based) | ~1 KB (only changed key) | ~1 MB | 🔥 30× smaller | | Redux (snapshots) | ~30 KB (entire object) | ~30 MB | ❌ copies everything | | Zustand (snapshots) | ~30 KB | ~30 MB | ❌ copies everything |


Benchmark: Varying Entropy (1–30 Attributes Changed, Neglecting setup overhead)

| Attributes Changed Per Update | Ledgex Stored per Update | Redux/Zustand Stored per Update | | --------------------------------- | ---------------------------- | ----------------------------------- | | 1 | ~1 KB | ~30 KB | | 5 | ~5 KB | ~30 KB | | 15 | ~15 KB | ~30 KB | | 30 | ~30 KB | ~30 KB |

Observation:

  • When only a few attributes change, Ledgex storage grows proportionally to the change.
  • Redux/Zustand remain constant (always full object copy).

Undo/Redo History Impact

Because Ledgex stores only deltas:

  • Undo memory = number of attributes changed × number of updates.
  • Redo is equally lightweight.
  • In Redux/Zustand, memory grows linearly with full object size × updates.

Support

Star the repo if you find it useful! 🐞 Report issues on GitHub