Skip to main content
Beta@videojs/store is close to stable. Experimental adoption in real projects is welcome.
@videojs/store provides reactive state management designed for use across different rendering environments. It uses a selector-based subscription model to minimize unnecessary re-renders and updates, and ships separate bindings for HTML/DOM and React environments.

Installation

npm install @videojs/store

Exports

Entry pointDescription
.Core — createSelector, shallowEqual, and types
./htmlHTML/DOM store bindings
./reactReact store bindings — useSelector, useStore

How it works

The store uses a selector-based subscription model. Rather than subscribing to the entire state tree, you define selectors that derive specific values. Subscribers are only notified when the selected value changes, as determined by a comparator function (default: shallowEqual).
store state → selector → derived value → comparator → subscriber callback

                                       only fires on change
This keeps your UI responsive while avoiding unnecessary work when unrelated parts of the state change.

Core API

createSelector

Creates a memoized selector function. Use selectors to derive specific values from state:
import { createSelector } from '@videojs/store';

const selectVolume = createSelector((state) => state.volume);
const selectIsPaused = createSelector((state) => state.paused);

// Compose selectors
const selectIsAudible = createSelector(
  (state) => state.volume > 0 && !state.muted
);

shallowEqual

The default comparator used by selectors. Performs a shallow equality check on objects and arrays:
import { shallowEqual } from '@videojs/store';

shallowEqual({ a: 1 }, { a: 1 }); // true
shallowEqual({ a: 1 }, { a: 2 }); // false
Provide a custom comparator to createSelector when you need stricter or looser equality:
import { createSelector } from '@videojs/store';

const selectTracks = createSelector(
  (state) => state.textTracks,
  (a, b) => a.length === b.length // custom comparator
);

React bindings (./react)

The React bindings integrate the store with React’s rendering model:
import { useSelector, useStore } from '@videojs/store/react';
import { createSelector } from '@videojs/store';

const selectPaused = createSelector((state) => state.paused);

function PlayButton() {
  const paused = useSelector(selectPaused);
  const store = useStore();

  return (
    <button onClick={() => store.dispatch({ type: paused ? 'play' : 'pause' })}>
      {paused ? 'Play' : 'Pause'}
    </button>
  );
}
ExportDescription
useSelector(selector)Subscribes a component to a slice of store state; re-renders only when the selected value changes
useStore()Returns the raw store instance

HTML bindings (./html)

The HTML bindings connect the store to DOM elements outside of a component framework:
import { createStoreController } from '@videojs/store/html';

const controller = createStoreController(store);

controller.subscribe(selectVolume, (volume) => {
  document.querySelector('.volume-display').textContent = `${Math.round(volume * 100)}%`;
});

Types

TypeDescription
Selector<State, Value>A function that derives a value from state
Comparator<Value>A function (a: Value, b: Value) => boolean used to detect changes

Peer dependencies

PackageRequiredNotes
@videojs/elementOptionalRequired only when using ./html bindings
react ^18 || ^19OptionalRequired only when using ./react bindings

@videojs/core

Core player logic that builds on top of this package.

@videojs/react

React package that re-exports useSelector and useStore.

@videojs/utils

Shared utilities used by this package.

@videojs/html

HTML package that uses the HTML store bindings.

Build docs developers (and LLMs) love