Skip to main content
SnapshotController subscribes to a State<T> container (the reactive primitive that backs a store) and triggers host updates when state changes. It has two overloads:
  • Without a selector — returns the entire state object. Re-renders on any state change.
  • With a selector — returns a derived slice of state. Re-renders only when the selected value changes (shallow equality).

Signature

import { SnapshotController } from '@videojs/store/html';

class SnapshotController<T extends object, R = T>
  implements ReactiveController

Constructor

new SnapshotController(host, state)
host
ReactiveControllerHost
required
The host element that owns this controller. Typically this inside a custom element class.
state
State<T>
required
The State container to subscribe to. Usually accessed as store.$state.

Properties

value
R
The current state value:
  • Without a selector: the full state object (T).
  • With a selector: the return value of the selector applied to the current state (R).
Unlike StoreController.value, this property never throws — the state container is provided at construction time.

Methods

track(state)
void
Switch to a different State<T> container at runtime. Tears down the previous subscription and sets up a new one immediately.
#state = new SnapshotController(this, initialState);

switchState(nextState: State<T>) {
  this.#state.track(nextState);
}

Usage

Full state subscription

import { SnapshotController } from '@videojs/store/html';

class SliderDisplay extends HTMLElement {
  readonly #state = new SnapshotController(this, sliderState);

  render() {
    const { value, min, max } = this.#state.value;
    this.textContent = `${value} / ${max}`;
  }
}

customElements.define('slider-display', SliderDisplay);

Selector-based subscription

import { SnapshotController } from '@videojs/store/html';

class SliderValue extends HTMLElement {
  readonly #value = new SnapshotController(
    this,
    sliderState,
    (s) => s.value
  );

  render() {
    // Only re-renders when s.value changes
    this.textContent = String(this.#value.value);
  }
}

customElements.define('slider-value', SliderValue);

Lifecycle

  • hostConnected() — subscribes to the State container.
  • hostDisconnected() — unsubscribes and clears the cached value.
The cached selector result is invalidated on disconnect, so reconnection always recomputes from the current state.

SnapshotController vs StoreController

SnapshotControllerStoreController
InputState<T> containerStore instance or context
SubscribesAlwaysOnly with a selector
Resolves storeNoYes (from context)
Use caseCustom controllers, raw statePlayer UI elements
SnapshotController is lower-level and works directly with State containers — the reactive primitives that back a store. Use it when building custom controllers or working outside the player store system. For player UI elements, use StoreController or PlayerController.
SnapshotController is the reactive primitive used internally by StoreController. When you pass a selector to StoreController, it creates a SnapshotController on store.$state under the hood.

Build docs developers (and LLMs) love