vis-data

A framework-agnostic reactive data layer for adding, updating, removing, and observing collections of structured JavaScript objects.

Library
npm
v8.0.5
119stars
(Apache-2.0 OR MIT)

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
87/100Excellent
Development Activity96
Maintenance96
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture82
Code Quality85
Innovation76
Learning Curve65

vis-data is the observable data layer that powers the vis.js visualization suite (vis-timeline, vis-network, vis-graph3d), giving those libraries — and any other JavaScript application — a key/value DataSet and a filtered, read-only DataView for holding unstructured collections of items. Items are added, updated, and removed through a small, well-typed API, and every mutation emits granular add/update/remove events that consumers can subscribe to, making it straightforward to keep a rendering layer in sync with changing data without re-fetching or diffing whole collections by hand.

Beyond the core DataSet/DataView pair, the package ships a Queue utility for batching rapid-fire mutations into a single flush, a DataPipe for declaratively transforming and forwarding items from one DataSet into another, and a DataStream for iterating, mapping, and filtering over a dataset’s contents lazily. The library is published as ESM and UMD builds with full TypeScript typings, and works equally well as the data backbone for a vis.js chart or as a standalone in-memory reactive store in any browser or Node.js project.

What You Get

  • A typed DataSet class for adding, updating, and removing items identified by a configurable id field
  • A DataView that exposes a live, filtered and/or mapped subset of a DataSet without duplicating the underlying data
  • Granular add, update, remove, and wildcard * events with before/after payloads for keeping UI or downstream stores in sync
  • A Queue helper that batches and flushes rapid mutation calls on a delay or size threshold
  • A DataPipe for declaratively piping and transforming items from one DataSet/DataView into another
  • A DataStream for lazily iterating, mapping, and filtering over dataset contents

Common Use Cases

  • Backing the data layer for a vis-timeline, vis-network, or vis-graph3d visualization so chart data can be mutated live
  • Maintaining an in-memory, observable collection of records in a browser app without pulling in a full state-management library
  • Deriving a filtered or sorted view of a larger dataset (e.g. only completed tasks) that stays in sync as the source set changes
  • Streaming data updates from a server (e.g. via WebSocket) directly into a DataSet so any subscribed view re-renders automatically
  • Piping and transforming records from one dataset into another dataset with a different shape via DataPipe

Under The Hood

Architecture vis-data is organized into small, single-responsibility modules rather than one monolithic class: data-set-part.ts implements a shared base (event emission via a mixed-in emitter and generic get/subscribe machinery) that both DataSet (data-set.ts) and the read-only DataView (data-view.ts) extend, so mutation logic lives once and filtering/projection logic lives once. DataInterface (data-interface.ts) is a pure type-only contract — Id, PartItem, FullItem, event payload types — that both concrete classes implement, giving strong compile-time guarantees around id handling. Cross-cutting concerns are pulled out into standalone collaborators rather than baked into DataSet: Queue batches and flushes rapid method calls, DataPipe (data-pipe.ts) wires a source DataSet/DataView’s add/update/remove events to transform-and-forward calls on a target DataSet, and DataStream (data-stream.ts) implements the iterator protocol for lazy traversal. Multiple build variants are selected purely through separate entry points (entry-esnext.ts, entry-peer.ts, entry-standalone.ts) rather than runtime branching, keeping the core modules free of packaging concerns. If the shared DataSetPart base or the DataInterface event-payload contract changed shape, every dependent — DataSet, DataView, DataPipe, and the downstream vis-timeline/vis-network/vis-graph3d consumers — would need to follow, making it the architecture’s clear central abstraction.

Tech Stack The package is pure TypeScript (ESM-first, type: module) with uuid (peer dependency, for generating item ids when none is supplied) and vis-util (peer dependency, supplying shared helpers like pureDeepObjectAssign) as its only two runtime dependencies. Builds run through Rollup (rollup.config.js for the peer/esnext bundles, rollup.build.js for a bundled ‘standalone’ variant with dependencies inlined) producing parallel ESM, UMD, minified, and TypeScript-declaration outputs under peer/, esnext/, and standalone/. Linting and formatting use oxlint/oxfmt — the newer Rust-based toolchain — enforced via Husky pre-commit hooks and lint-staged rather than ESLint/Prettier. Documentation is generated with TypeDoc straight from the extensive inline JSDoc. CI runs on CircleCI (unit tests, an interop smoke test against the built output, and build/publish stages) plus a GitHub Actions workflow that auto-applies lint/style fixes on pull requests.

Code Quality Testing uses Mocha with Chai assertions and Sinon for spies/stubs, run under Babel for TS/ESM transpilation, with an nyc-based coverage config and a dedicated test:interop step that sanity-checks the actual built package output rather than only source. The suite spans over a dozen test files covering DataSet, DataView, DataPipe, Queue, chained-event behavior, and package-shape checks, with a handful of tests still in legacy JS form alongside newer typed TS tests — indicating an in-progress but largely complete migration to TypeScript-native testing. Source files carry unusually dense JSDoc across the src/ directory, and generics are used consistently to make id-field typing (fieldId) flow through DataSet/DataView/DataStream without casting. Error handling favors explicit thrown exceptions for misuse (e.g. updating an item with no id) over silent no-ops, matched by tests that assert the throw behavior directly.

API Design The library’s central ergonomic choice is separating a mutable, owned DataSet from a read-only, live DataView that can filter and/or project fields — so consumers get a selector-like derived view without manual re-filtering or subscription bookkeeping, and it composes (a DataView can itself be the source for another DataView or a DataPipe). Getting started requires only new DataSet(items) plus, optionally, .on('*', handler) — there’s no store/reducer/action boilerplate. Where it invests less in novelty is that the underlying model (typed key/value collection plus subscribe-to-mutations) is conceptually close to other reactive/observable-collection libraries in the JS ecosystem; vis-data’s specific value is being purpose-built as the shared data spine for the vis.js chart family, with the id-normalization and queueing behavior those visualizations specifically need.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search