ng-hub-ui-history
v22.0.1
Published
Signal-based multi-object undo/redo history store for Angular applications
Maintainers
Readme
ng-hub-ui-history
Español | English
Signal-based history store for Angular with multi-object support, undo/redo, transactions, and automatic Reactive Forms tracking.
Documentation and Live Examples
This package is part of Hub UI, a collection of Angular component libraries for standalone apps.
- Docs: https://hubui.dev/history/overview/
- Live examples: https://hubui.dev/history/examples/
- Hub UI: https://hubui.dev/
🧩 Library Family ng-hub-ui
This library is part of the ng-hub-ui ecosystem:
- ng-hub-ui-accordion (deprecated — use ng-hub-ui-panels)
- ng-hub-ui-action-sheet
- ng-hub-ui-avatar
- ng-hub-ui-board
- ng-hub-ui-breadcrumbs
- ng-hub-ui-calendar
- ng-hub-ui-dropdown
- ng-hub-ui-ds
- ng-hub-ui-forms
- ng-hub-ui-history ← You are here
- ng-hub-ui-milestones
- ng-hub-ui-modal
- ng-hub-ui-nav
- ng-hub-ui-paginable
- ng-hub-ui-panels
- ng-hub-ui-portal
- ng-hub-ui-skeleton
- ng-hub-ui-sortable
- ng-hub-ui-stepper
- ng-hub-ui-utils
📋 Table of Contents
Description
ng-hub-ui-history is a framework-agnostic, signal-based history store for Angular
applications. It tracks the state of any number of objects independently, each keyed by a
configurable identifier, and exposes a fully reactive states signal that always reflects
the current snapshot of every tracked object.
Instead of storing full snapshots per change, the store records compact forward/backward
diff patches, which keeps memory usage low while supporting linear undo/redo navigation.
Retention is bounded by both a maximum number of entries and an approximate memory budget.
The store also provides transactions to collapse many updates into a single history entry
and a watchForm helper that auto-commits Angular Reactive Forms value changes.
This library ships no visual components — it is a pure logic/store package, so there is no CSS-variables section.
Features
- Multi-object tracking by configurable key type.
- Efficient history entries using diff patches instead of full snapshots.
- Undo/redo with linear timeline behavior.
- Redo invalidation when new manual commits happen after undo.
- Retention controls by number of entries and approximate memory bytes.
- Transaction support to group many updates into a single history entry.
- Optional custom diff/patch strategies.
- Automatic Reactive Forms integration via
watchForm. - Reactive
statessignal exposing the current snapshot of every tracked object.
Installation
npm install ng-hub-ui-historyPeer dependencies: @angular/common, @angular/core, @angular/forms (>=18.0.0) and rxjs (>=7.5.0).
Quick Start
import { createHistoryStore } from 'ng-hub-ui-history';
interface Note {
id: string;
name: string;
}
const store = createHistoryStore<Note, string>({
maxEntries: 100,
maxBytes: 250_000,
keySelector: (state) => state.id
});
// Register an object to start tracking its history.
store.registerObject('a-1', { id: 'a-1', name: 'Initial' });
// Commit a new state. Returns false when nothing changed.
store.commit('a-1', { id: 'a-1', name: 'Edited' }, { label: 'Rename' });
// Navigate the timeline.
store.undo('a-1'); // -> { id: 'a-1', name: 'Initial' }
store.redo('a-1'); // -> { id: 'a-1', name: 'Edited' }
// Read the current state at any time.
const current = store.getState('a-1');
// React to changes via the signal.
const all = store.states(); // Map<string, Note>Transactions
store.beginTransaction('a-1', 'Bulk edit');
store.commit('a-1', { id: 'a-1', name: 'Step 1' });
store.commit('a-1', { id: 'a-1', name: 'Step 2' });
store.endTransaction('a-1'); // Stored as a single history entry.Watching a Reactive Form
const form = new FormGroup({ name: new FormControl('Initial') });
store.registerObject('form-1', form.value as Note);
// Auto-commit every value change; returns an unsubscribe function.
const stop = store.watchForm('form-1', form, { skipInitial: true, label: 'Form edit' });
// Later, stop tracking.
stop();API Reference
createHistoryStore<T, K>(config?)
Creates a history store instance. T is the tracked object type and K is the key type
(defaults to string | number).
HistoryStoreConfig<T, K>
| Option | Type | Default | Description |
| ------------- | ------------------------------------------------------------- | --------- | ---------------------------------------------------- |
| maxEntries | number | 100 | Maximum number of entries per object history. |
| maxBytes | number | 512000 | Maximum approximate bytes per object history. |
| keySelector | (state: T) => K | — | Key resolver used by the *FromObject helpers. |
| diff | (previous: T, next: T) => HistoryPatchOperation[] | built-in | Optional custom diff strategy. |
| patch | (current: T, operations: HistoryPatchOperation[]) => T | built-in | Optional custom patch strategy. |
Store API (HistoryStore<T, K>)
| Member | Returns | Description |
| --------------------------------------- | --------------------- | --------------------------------------------------------------------------- |
| states | Signal<Map<K, T>> | Reactive dictionary of current states keyed by object id. |
| registerObject(id, initialState) | void | Registers a tracked object and starts a fresh timeline. |
| registerFromObject(initialState) | K | Registers using the configured keySelector; returns the resolved id. |
| commit(id, newState, options?) | boolean | Commits a new snapshot. Returns false when nothing changed. |
| commitFromObject(newState, options?) | boolean | Commits using the configured keySelector. |
| undo(id) | boolean | Reverts one step. Returns false at the timeline base. |
| redo(id) | boolean | Re-applies one step. Returns false when no redo entry exists. |
| canUndo(id) | boolean | Whether undo is currently possible. |
| canRedo(id) | boolean | Whether redo is currently possible. |
| getState(id) | T \| undefined | Current immutable state, or undefined when not registered. |
| history(id) | HistoryMetadata | Readonly metadata (pointer, length, bytes, per-entry info). |
| beginTransaction(id, label?) | void | Starts a transaction that merges subsequent commits into one entry. |
| endTransaction(id) | boolean | Ends the transaction, storing a single consolidated entry. |
| watchForm(id, form, options?) | () => void | Auto-commits Reactive Form value changes; returns an unsubscribe function. |
| clearHistory(id) | void | Clears all entries and keeps the current state as the new base. |
HistoryCommitOptions
| Property | Type | Description |
| -------- | -------- | ------------------------------------------------- |
| label | string | Optional label stored in history metadata and UI. |
WatchFormOptions
| Property | Type | Description |
| ------------- | --------- | ---------------------------------------------------- |
| label | string | Optional label used for each automatic commit. |
| skipInitial | boolean | Whether to ignore the first value emission. |
HistoryMetadata
| Property | Type | Description |
| --------- | ------------------------------------------------------------- | -------------------------------------------------- |
| pointer | number | Current pointer; -1 means the base snapshot. |
| length | number | Total entries retained in memory. |
| bytes | number | Accumulated approximate bytes retained. |
| entries | Array<Pick<HistoryEntry, 'label' \| 'timestamp' \| 'bytes'>> | Human-readable per-entry metadata. |
Additional exported types: HistoryEntry, HistoryPatchOperation.
Changelog
See CHANGELOG.md for the full release history.
Contribution
We welcome all contributions! Here's how you can help:
Getting Started
# Clone the repository
git clone https://github.com/carlos-morcillo/ng-hub-ui-history.git
cd ng-hub-ui-history
# Install dependencies
npm install
# Run tests
npm run testContributing Guidelines
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Add tests for your changes
- Ensure all tests pass:
npm run test - Commit your changes:
git commit -m 'Add amazing feature' - Push to your branch:
git push origin feature/amazing-feature - Submit a pull request
Reporting Issues
When reporting bugs, please include:
- Angular version
- Steps to reproduce
- Expected vs actual behavior
- Minimal reproduction example (StackBlitz preferred)
Support
Do you like this library? You can support us by buying us a coffee ☕:

- Hub UI docs: https://hubui.dev/history/overview/
- Live examples: https://hubui.dev/history/examples/
License
This project is licensed under the MIT License - see the LICENSE file for details.
MIT © ng-hub-ui contributors
