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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@streamiq/react

v1.1.0

Published

React bindings for the Streamq playback SDK.

Readme

@streamiq/react

React bindings for the Streamq playback SDK — StreamqProvider, external stores, RAF-batched event-to-store bindings, granular hooks, and headless <Video> / <Audio> / <PlayerRoot>.

Stable 1.0.x. Requires @streamiq/core. Install @streamiq/hls or @streamiq/dash for adaptive streaming.

Install

npm install @streamiq/core @streamiq/react react react-dom
npm install @streamiq/hls   # or @streamiq/dash

Quick start

import { createHlsPlaybackAdapter } from '@streamiq/hls';
import {
  StreamqProvider,
  PlayerRoot,
  Video,
  usePlay,
  usePause,
  usePlaybackState,
  useCurrentTime,
} from '@streamiq/react';

function Controls() {
  const state = usePlaybackState();
  const time = useCurrentTime();
  const play = usePlay();
  const pause = usePause();

  return (
    <div>
      <button type="button" onClick={play}>Play</button>
      <button type="button" onClick={pause}>Pause</button>
      <span>{state} · {time.toFixed(1)}s</span>
    </div>
  );
}

export default function App() {
  return (
    <StreamqProvider
      options={{
        createPlaybackAdapter: createHlsPlaybackAdapter,
        autoplay: true,
        source: { src: 'https://example.com/master.m3u8', type: 'hls' },
      }}
    >
      <PlayerRoot>
        <Video playsInline />
      </PlayerRoot>
      <Controls />
    </StreamqProvider>
  );
}

There is no monolithic StreamqPlayer export here — compose StreamqProvider + media + your UI. For a drop-in cinematic preset, see @streamiq/player.


Architecture

@streamiq/react/src
├── provider/
│   └── StreamqProvider.ts              Mounts Player, owns stores, provides context
│
├── context/
│   └── StreamqContext.ts               { player, stores } container
│
├── media/                              Headless media elements
│   ├── PlayerRoot.ts                   Container + media-element ref handoff
│   ├── Video.ts                        <video> with Streamq wiring
│   ├── Audio.ts                        <audio> with Streamq wiring
│   └── MediaContext.ts                 Element context for nested components
│
├── stores/                             External stores (useSyncExternalStore-friendly)
│   ├── createStore.ts                  Generic factory (WritableStore, ExternalStore)
│   ├── playbackStore.ts (+ types)      time, duration, paused, seeking, ended, buffered
│   ├── volumeStore.ts (+ types)        volume, muted
│   ├── qualityStore.ts (+ types)       levels, active, auto, capabilities
│   ├── audioTrackStore.ts              tracks, active
│   ├── subtitleStore.ts (+ types)      tracks, active, style
│   ├── cueStore.ts (+ types)           active cues, primary cue
│   ├── pipStore.ts (+ types)           supported, active
│   ├── fullscreenStore.ts (+ types)    active, request element
│   └── batchStoreUpdates.ts            BatchMode + flushRafNow
│
├── bindings/                           Wire Player events → stores
│   ├── bindPlayerToStores.ts           Orchestrator
│   ├── playbackBindings.ts
│   ├── volumeBindings.ts
│   ├── qualityBindings.ts
│   ├── audioTrackBindings.ts
│   ├── subtitleBindings.ts
│   ├── cueBindings.ts
│   ├── pipBindings.ts
│   ├── fullscreenBindings.ts
│   └── rafSynchronizer.ts              createRAFSynchronizer for high-rate updates
│
├── selectors/                          Selector pattern over stores
│   ├── createSelector.ts               EqualityFn-aware
│   ├── playbackSelectors.ts            selectCurrentTime, selectPaused, …
│   ├── shallowEqual.ts
│   └── useStoreSelector.ts             + useShallowSelector
│
├── hooks/                              Public hook surface
│   ├── usePlayer.ts / usePlayerContext.ts
│   ├── usePlayback.ts / usePlaybackSelectors.ts
│   ├── usePlaybackControls.ts          play / pause / toggle / seek / restart
│   ├── useVolumeControls.ts            volume / muted / set / toggle mute
│   ├── useFullscreen.ts                fullscreen state + actions
│   ├── usePiP.ts                       PiP state + actions
│   ├── useQuality.ts                   levels, active, auto, select
│   ├── useAudioTrack.ts                tracks, current, select
│   ├── useTextTrack.ts                 tracks, active, select, disable
│   ├── useCue.ts                       active cue / cues
│   ├── useSubtitleAnnouncer.ts         a11y live region helper
│   ├── useBufferingAnnouncer.ts        a11y live region helper
│   ├── useMediaReady.ts                metadata / canPlay readiness
│   ├── useAbortSignal.ts               Per-effect AbortSignal
│   ├── useHydrated.ts                  SSR safety
│   └── useDeferredBrowser.ts           Defer browser-only logic
│
├── debug/                              Tree-shakeable instrumentation (no-op when off)
│   ├── debugCore.ts                    __streamqDebug, getDebugStats, …
│   ├── debugReporter.ts
│   ├── useDebugEvents.ts
│   ├── useDebugPlayback.ts
│   ├── useStoreDebug.ts
│   └── useRenderDebug.ts
│
├── utils/
│   ├── env.ts                          isBrowser
│   ├── subscriptionGuard.ts            createSubscriptionGuard
│   ├── structuralSharing.ts
│   └── throttle.ts
│
└── types/                              Public types (context.types, media.types)

Data flow

Player (core)
   │ events (mitt)
   ▼
bindPlayerToStores()
   ├─ playbackBindings  ──► playbackStore   (RAF-batched timeupdate via createRAFSynchronizer)
   ├─ volumeBindings    ──► volumeStore
   ├─ qualityBindings   ──► qualityStore
   ├─ audioTrackBindings──► audioTrackStore
   ├─ subtitleBindings  ──► subtitleStore
   ├─ cueBindings       ──► cueStore
   ├─ pipBindings       ──► pipStore
   └─ fullscreenBindings──► fullscreenStore
   │
   ▼
useStoreSelector / granular hooks (subscribe to one slice)
   │
   ▼
Your app UI (components re-render only on the slice they read)

Rules

  • PlayerContext carries Player | null plus stores — no duplicated FSM in React.
  • Hooks subscribe to specific store slices to minimize re-renders.
  • Adapters are composed at the app edge via createPlaybackAdapter.
  • All hooks are SSR-safe; useHydrated() and useDeferredBrowser() cover hydration and window access.

StreamqProvider

<StreamqProvider options={playerFactoryOptions}>
  {children}
</StreamqProvider>

| Prop | Type | Description | |------|------|-------------| | options | PlayerFactoryOptions | Everything except target (set via PlayerRoot / Video) | | children | ReactNode | App tree |

Common options fields:

| Field | Description | |-------|-------------| | createPlaybackAdapter | Factory from @streamiq/hls or @streamiq/dash | | source | Initial source (optional — can also player.load() imperatively) | | autoplay | Autoplay after load | | plugins | Array of StreamqPlugin instances | | recovery | Load retry / fallback policy |


Media components

| Component | Role | |-----------|------| | PlayerRoot | Attaches Player to a media element ref; provides container context | | Video | <video> with Streamq wiring | | Audio | <audio> with Streamq wiring | | MediaContext | Element context for nested components |


Hooks reference

Player access

| Hook | Returns | |------|---------| | usePlayer() | Player instance (imperative API) | | usePlayerContext() | Full context (stores + player) |

Playback

| Hook | Returns | |------|---------| | usePlaybackState() | 'idle' \| 'loading' \| 'ready' \| 'playing' \| … | | usePlayback(selector) | Custom selector over playback store | | useCurrentTime() | Current time (seconds) | | useDuration() | Duration (seconds) | | useBuffered() | Buffered end (seconds) | | usePaused() | Whether paused | | useSeeking() | Whether seeking | | useEnded() | Whether ended | | usePlay() | Stable () => void play callback | | usePause() | Stable pause callback | | useTogglePlayback() | Toggle play/pause | | useSeek() | (time: number) => void | | useRestart() | Seek to start |

Volume

| Hook | Returns | |------|---------| | useVolume() | 0–1 | | useMuted() | boolean | | useSetVolume() | (v: number) => void | | useToggleMute() | toggle callback |

Fullscreen & PiP

| Hook | Returns | |------|---------| | useFullscreen() | { isFullscreen, enterFullscreen, exitFullscreen, toggleFullscreen } | | usePiP() | { isPiP, canPiP, enterPiP, exitPiP, togglePiP } |

Quality

| Hook | Returns | |------|---------| | useQualities() | Full qualities snapshot | | useQualityLevels() | Available levels array | | useCurrentQuality() | Active quality id | | useAutoQuality() | Whether ABR is active | | useSelectQuality() | (id: string) => void |

Subtitles & text tracks

| Hook | Returns | |------|---------| | useTextTracks() | Discovered tracks | | useActiveTextTrack() | Active track or null | | useSelectTextTrack() | Select by id | | useDisableTextTrack() | Turn captions off | | useActiveCues() | Current cue list | | useActiveCue() | Primary active cue | | useCue() | Alias for active cue |

Audio tracks

| Hook | Returns | |------|---------| | useAudioTracks() | Full snapshot | | useAudioTrackList() | Track list | | useCurrentAudioTrack() | Active track id | | useSelectAudioTrack() | Select by id |

SSR & browser safety

| Hook | Returns | |------|---------| | useHydrated() | false during SSR/hydration, true after | | useDeferredBrowser(fn, fallback) | Defer browser-only logic | | useMediaReady() | MediaReadyState (metadata / canPlay) | | useAbortSignal() | AbortSignal for async effects |


Advanced: stores & bindings

For custom integrations, the package exposes the store layer:

| Export | Role | |--------|------| | createPlaybackStore, createStore | External store factories | | bindPlayerToStores, BindOptions, PlayerStores | Wire Player events → stores | | bindPlaybackEvents, bindVolumeEvents, bindQualityEvents, bindAudioTrackEvents, bindSubtitleEvents, bindCueEvents, bindPiPEvents, bindFullscreenEvents | Per-domain bindings | | createRAFSynchronizer | rAF-batched time updates | | batchStoreUpdates, flushRafNow, BatchMode | Batch React updates | | createSelector, useStoreSelector, useShallowSelector, EqualityFn | Selector pattern | | selectPlaybackState, selectCurrentTime, selectDuration, selectBuffered, selectPaused, selectSeeking, selectEnded, shallowEqual | Pre-built selectors | | Snapshot types | PlaybackSnapshot, VolumeSnapshot, CueSnapshot, SubtitleSnapshot, PiPSnapshot, FullscreenSnapshot, WritableStore, ExternalStore |


Debug (tree-shakeable)

| Export | Role | |--------|------| | __streamqDebug | Debug namespace | | useDebugEvents, useDebugPlayback, useRenderDebug, useStoreDebug | Dev probes | | getDebugStats, resetDebugStats, printDebugSummary, startRenderBudgetMonitor | Stats helpers | | DebugEvent, DebugEventType, DebugListener, DebugStats, PlaybackDebugInfo, PlayerEvent | Types |

When __streamqDebug.enabled = false, debug code paths are eliminated by the bundler.


SSR / Next.js

  • All hooks are SSR-safe
  • Use useHydrated() before rendering browser-only UI
  • Use useDeferredBrowser() for APIs that touch window / document
  • Call player.load() after mount if not using options.source

Custom UI

Wire your own controls to hooks — no bundled UI in this package:

import { StreamqProvider, PlayerRoot, Video, usePlay, usePause, usePlaybackState } from '@streamiq/react';
import { createHlsPlaybackAdapter } from '@streamiq/hls';

function PlayControl() {
  const play = usePlay();
  const pause = usePause();
  const state = usePlaybackState();
  return state === 'playing' ? (
    <button type="button" onClick={() => pause()}>Pause</button>
  ) : (
    <button type="button" onClick={() => play()}>Play</button>
  );
}

<StreamqProvider options={{ createPlaybackAdapter: createHlsPlaybackAdapter }}>
  <PlayerRoot>
    <Video />
    <PlayControl />
  </PlayerRoot>
</StreamqProvider>

For a polished default UI, install @streamiq/ui primitives and/or the @streamiq/player preset.


Dependencies

| | | |-|-| | Runtime | @streamiq/core | | Peer | react ≥ 18, react-dom ≥ 18 |

Adapters (@streamiq/hls, @streamiq/dash) are not bundled — install separately.

License

MIT