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

@nurav/player-react

v0.0.3

Published

Drop-in React HLS player component with full controls, gesture handling, keyboard shortcuts, token auth, and customizable themes.

Readme

@nurav/player-react

A complete, ready-to-use React HLS video player component. Drop it in, give it a stream URL, and you get a fully functional video player with controls, gestures, keyboard shortcuts, and customizable themes.

This package bundles @nurav/player-core (video engine) and @nurav/player-ui (UI components) into one easy-to-use component.

# npm
npm install @nurav/player-react @nurav/player-core @nurav/player-ui

# yarn
yarn add @nurav/player-react @nurav/player-core @nurav/player-ui

# pnpm
pnpm add @nurav/player-react @nurav/player-core @nurav/player-ui

# bun
bun add @nurav/player-react @nurav/player-core @nurav/player-ui

Quick Start

The simplest possible player — just one line:

import { HlsPlayer } from "@nurav/player-react";
import "@nurav/player-ui/styles"; // ← Required! Loads the CSS

function App() {
  return (
    <HlsPlayer
      src="https://example.com/stream.m3u8"
      style={{ width: "100%", maxWidth: 800 }}
    />
  );
}

That's all you need. Your video will load with play/pause, seek, volume, settings, and fullscreen controls.


What You Get

When you use <HlsPlayer />, you get all of this out of the box:

| Feature | Description | | --------------------- | -------------------------------------------------- | | 🎬 Play/Pause | Click the video or press Space | | ⏪⏩ Seek | Drag the progress bar or use arrow keys | | 🔊 Volume | Slider with mute toggle | | ⚙️ Settings | Speed and quality picker | | 🖥️ Fullscreen | Toggle fullscreen mode | | ⌨️ Keyboard | Space (play/pause), F (fullscreen), arrows (seek) | | 📱 Mobile | Tap zones, swipe gestures, bottom sheet settings | | 🎯 Touch Gestures | Tap to play/pause, swipe to seek | | 🔴 Live Streams | DVR support, live edge indicator, "Go Live" button | | 🔐 Token Auth | Play protected streams | | 🎨 Themes | Custom colors and styles | | 🔄 Auto Buffering | Shows loading spinner when buffering |


Installation

# npm
npm install @nurav/player-react @nurav/player-core @nurav/player-ui

# yarn
yarn add @nurav/player-react @nurav/player-core @nurav/player-ui

# pnpm
pnpm add @nurav/player-react @nurav/player-core @nurav/player-ui

# bun
bun add @nurav/player-react @nurav/player-core @nurav/player-ui

This automatically installs @nurav/player-core and @nurav/player-ui.

Load the CSS

The player will be invisible without this import. Add it once in your app:

// At the top of your main component or App.tsx
import "@nurav/player-ui/styles";

Simple Examples

Basic Player

import { HlsPlayer } from "@nurav/player-react";
import "@nurav/player-ui/styles";

function VideoPlayer() {
  return (
    <div style={{ width: "100%", maxWidth: 800 }}>
      <HlsPlayer
        src="https://example.com/live/stream.m3u8"
        style={{ width: "100%", aspectRatio: "16 / 9" }}
      />
    </div>
  );
}

Auto-Play (Muted)

<HlsPlayer src="https://example.com/stream.m3u8" autoPlay muted />

With Poster Image

<HlsPlayer
  src="https://example.com/stream.m3u8"
  poster="https://example.com/thumbnail.jpg"
/>

Start at a Specific Time

<HlsPlayer
  src="https://example.com/stream.m3u8"
  startTime={120} // Start at 2 minutes
/>

Custom Colors

<HlsPlayer
  src="https://example.com/stream.m3u8"
  themeOverrides={{
    "--vp-accent": "#ec4899", // Pink accent
    "--vp-radius": "12px",
  }}
/>

Custom Playback Speeds

<HlsPlayer
  src="https://example.com/stream.m3u8"
  playbackRates={[0.5, 1, 1.5, 2, 3]}
/>

With Player Ref (Accessing Player API)

import { useRef } from "react";
import { HlsPlayer } from "@nurav/player-react";
import type { Player } from "@nurav/player-core";

function PlayerWithControls() {
  const playerRef = useRef<Player>(null);

  return (
    <div>
      <HlsPlayer ref={playerRef} src="https://example.com/stream.m3u8" />

      <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
        <button onClick={() => playerRef.current?.togglePlay()}>
          Play/Pause
        </button>
        <button onClick={() => playerRef.current?.setPlaybackRate(2)}>
          2x Speed
        </button>
        <button onClick={() => playerRef.current?.seekToLive()}>Go Live</button>
      </div>
    </div>
  );
}

All Props

Core

| Prop | Type | Default | Description | | ---------------- | --------------- | ------------ | ----------------------------------------- | | src | string | required | HLS stream URL (.m3u8) | | autoPlay | boolean | false | Start playing automatically | | muted | boolean | false | Start muted | | poster | string | — | Thumbnail/poster image URL | | startTime | number | — | Start at this time (seconds) | | className | string | — | Extra CSS class for the player | | videoClassName | string | — | Extra CSS class for the <video> element | | style | CSSProperties | — | Inline styles for the player container |

Stream Configuration

| Prop | Type | Default | Description | | ------------------ | -------------- | ------------------------------------ | ----------------------------------------------------------------------- | | lowLatency | boolean | false | Enable low-latency HLS mode | | liveSyncDuration | number | 5 | Seconds behind live to show "Go Live" | | tokenFetcher | TokenFetcher | — | Auth function for protected streams | | playbackRates | number[] | [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2] | Available speeds | | live | LiveConfig | {} | Live engine settings: { syncDuration?: number, lowLatency?: boolean } |

UI Configuration

| Prop | Type | Default | Description | | ------------------- | --------------------- | ------- | -------------------------------------------------------------------------------- | | controls | boolean | true | Show the control bar | | keyboard | boolean | true | Enable keyboard shortcuts | | seekStep | number | 10 | Seconds to seek per arrow press | | theme | "kgs" | "kgs" | Theme to use | | themeOverrides | ThemeVars | — | Custom CSS colors | | customization | PlayerCustomization | — | Show/hide specific controls | | disableDevOptions | boolean | false | Block DevTools, F12 hotkeys, dragging, and context menus. Auto-resumes on close. |

Advanced

| Prop | Type | Default | Description | | ---------------- | ---------------------- | -------------------------- | ------------------------------- | | centerZoneX | { start, end } | { start: 0.4, end: 0.6 } | Horizontal zone for tap-to-play | | centerZoneY | { start, end } | { start: 0.4, end: 0.6 } | Vertical zone for tap-to-play | | onPlayerReady | (player) => void | — | Called when player is ready | | renderControls | (props) => ReactNode | — | Replace the entire control bar | | root | HTMLElement | — | Element for fullscreen API |


Token Auth (Protected Streams)

If your video requires authentication, provide a tokenFetcher:

import { HlsPlayer, type TokenFetcher } from "@nurav/player-react";

const videoId = 527697;

const tokenFetcher: TokenFetcher = async ({ signal }) => {
  const res = await fetch(`https://api.example.com/video/${videoId}`, {
    signal,
  });
  const data = await res.json();
  if (!data.video_url) {
    throw new Error(data.message || "Access denied");
  }
  return { url: data.video_url };
};

function Player() {
  return <HlsPlayer src="placeholder" tokenFetcher={tokenFetcher} autoPlay />;
}

Enterprise Security (Secure Dev Shield)

Protect your video streams and learning assets from scraping or unauthorized access by enabling the disableDevOptions prop:

<HlsPlayer src="https://example.com/stream.m3u8" disableDevOptions={true} />

When active, the player launches multiple layers of defense:

  1. Event Interception: Blocks F12, view source, element selection, inspect tools, right-clicks/context menus, and asset drag-and-drop.
  2. Active Checking Traps: Periodically monitors browser resizing (detecting side-docked panels >250px) and runs debugger; statement execution timing loops. If thread halt is detected (>200ms), the player enters a lock state.
  3. Actions & Auto-Recovery:
    • Lock Screen: Pauses playback, toggles the store's isDevtoolsDetected state to true, and renders a blurred glassmorphic security overlay.
    • Auto-Resume: As soon as developer tools are closed, the player automatically clears the lock state and resumes video playback instantly.

Custom Controls

You can replace the entire control bar with your own:

<HlsPlayer
  src="https://example.com/stream.m3u8"
  renderControls={({ player, state, seekRelative, formatTime }) => (
    <div style={{ display: "flex", gap: 8, padding: 8, background: "#222" }}>
      <button onClick={() => player?.togglePlay()}>
        {state?.isPlaying ? "⏸" : "▶"}
      </button>
      <input
        type="range"
        min={0}
        max={state?.duration || 0}
        value={state?.currentTime || 0}
        onChange={(e) => player?.seek(Number(e.target.value))}
        style={{ flex: 1 }}
      />
      <span style={{ color: "#fff" }}>
        {formatTime(state?.currentTime || 0)}
      </span>
    </div>
  )}
/>

Keyboard Shortcuts

| Key | Action | | ------- | ------------------------------ | | Space | Play / Pause | | F | Toggle fullscreen | | M | Mute / Unmute | | | Seek backward (10s by default) | | | Seek forward (10s by default) | | | Volume up | | | Volume down | | S | Toggle stretch / fit |


Using the Hooks (Headless)

If you don't want the built-in UI, you can use the useHlsPlayer hook:

import { useHlsPlayer } from "@nurav/player-react";

function CustomPlayer() {
  const { player, state, error, rootRef, videoRef } = useHlsPlayer({
    src: "https://example.com/stream.m3u8",
    autoPlay: true,
  });

  return (
    <div
      ref={rootRef}
      style={{ width: 640, height: 360, position: "relative" }}
    >
      <video ref={videoRef} style={{ width: "100%", height: "100%" }} />

      <div style={{ position: "absolute", bottom: 8, left: 8, color: "#fff" }}>
        {state?.isPlaying ? "▶ Playing" : "⏸ Paused"}
        {" | "}
        {state?.currentTime?.toFixed(1)}s / {state?.duration?.toFixed(1)}s
      </div>

      {error && (
        <div style={{ color: "red", padding: 8 }}>Error: {error.message}</div>
      )}
    </div>
  );
}

Customization Reference

PlayerCustomization

Fine-tune which controls are visible:

type PlayerCustomization = {
  showPlayButton?: boolean; // Show play/pause in control bar
  showTimeDisplay?: boolean; // Show current time / duration
  showSettings?: boolean; // Show settings gear button
  showFullscreen?: boolean; // Show fullscreen toggle
  showCenterOverlay?: boolean; // Show center play/pause overlay
  showObjectFitButton?: boolean; // Show video fit toggle
  volumeControl?: "horizontal" | "vertical" | "hidden";
  centerOverlayGap?: number; // Gap between center buttons (px)
  objectFit?: "contain" | "cover" | "fill";
};

ThemeVars (CSS Variables)

Override any visual property:

type ThemeVars = {
  "--vp-accent"?: string; // Primary color
  "--vp-accent-contrast"?: string; // Text color on accent
  "--vp-surface"?: string; // Control bar background
  "--vp-border"?: string; // Border color
  "--vp-text"?: string; // Text color
  "--vp-muted"?: string; // Secondary text
  "--vp-radius"?: string; // Border radius
  "--vp-control-radius"?: string; // Button radius
  "--vp-video-bg"?: string; // Video background
};

TypeScript

All types are exported:

import type { Player } from "@nurav/player-core";
import type { LiveConfig } from "@nurav/player-core";
import type {
  TokenFetcher,
  PlayerCustomization,
  ThemeVars,
} from "@nurav/player-react";

Troubleshooting

| Problem | Solution | | -------------------------------------- | ----------------------------------------------- | | Player is invisible / no controls show | Did you import "@nurav/player-ui/styles"? | | Controls show but video doesn't load | Check the src URL. Must end in .m3u8 | | "Access denied" error | You need a tokenFetcher for protected streams | | Video stutters or buffers a lot | Try lowLatency prop or check network speed | | Player is too small / large | Set width and aspectRatio via style prop |


License

MIT