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

@zezosoft/react-player

v0.0.10

Published

A lightweight and customizable video player by Zezosoft, built for seamless streaming with advanced controls, adaptive playback, and modern UI. Perfect for web and React applications.

Downloads

289

Readme

@zezosoft/react-player 🎬

A powerful and flexible React video player by Zezosoft, supporting HLS, MP4, DASH, YouTube, preview thumbnails, tracking, subtitles, episode playback, ads, and advanced controls.


🚀 Features

Multiple Video Formats - HLS, MP4, DASH, YouTube
Preview Thumbnails on Hover - Show video previews while scrubbing
Event Tracking - Track views, watch time, and user interactions
Customizable Player Size & Controls - Full control over player appearance
Time-Stamped Labels - Video chapters with time markers
Subtitles (WebVTT) - Multi-language subtitle support with custom styling
Intro Skipping - Automatic skip intro button
Episode Playback - Next episode auto-play and playlist support
Ad Support - Pre-roll, mid-roll, post-roll, and overlay ads
Watch History - Track user progress and resume playback


📦 Installation

Install the package using npm or yarn:

npm install @zezosoft/react-player

or

yarn add @zezosoft/react-player

🛠️ Quick Start

Here's a simple example to get you started:

import { VideoPlayer } from "@zezosoft/react-player";

function App() {
  return (
    <VideoPlayer
      video={{
        src: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
        title: "My Video",
        poster: "https://example.com/poster.jpg",
      }}
      style={{
        width: "100%",
        height: "auto",
      }}
    />
  );
}

📖 Complete Example

Here's a comprehensive example showing all available features:

import { useCallback, useRef } from "react";
import { VideoPlayer } from "@zezosoft/react-player";

function App() {
  const previewImage = useRef("");

  const updatePreviewImage = (hoverTime: number) => {
    const url = `https://fakeimg.pl/720x405?text=${hoverTime}`;
    const image = document.createElement("img");
    image.src = url;
    image.onload = () => {
      previewImage.current = url;
    };
  };

  const handleGettingPreview = useCallback((hoverTime: number) => {
    updatePreviewImage(hoverTime);
    return previewImage.current;
  }, []);

  return (
    <div style={{ width: "720px", margin: "0 auto" }}>
      <VideoPlayer
        video={{
          src: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
          title: "Mehmaan",
          poster: "https://i.ytimg.com/vi/VAUfyxw-Yvk/maxresdefault.jpg",
          type: "hls",
          isTrailer: false,
          showControls: true,
          isMute: false,
          startFrom: 0,
        }}
        style={{
          width: "720px",
          height: "405px",
          className: "my-video-player",
          subtitleStyle: {
            fontSize: "1.5rem",
            backgroundColor: "rgba(0, 0, 0, 0.8)",
            textColor: "#ffffff",
            position: "bottom",
            borderRadius: "8px",
            padding: "8px 16px",
            maxWidth: "80%",
          },
        }}
        events={{
          onEnded: (e) => console.log("Video ended", e),
          onError: (e) => console.error("Video error", e),
          onClose: () => console.log("Player closed"),
          onWatchHistoryUpdate: (data) => {
            console.log("Watch history:", data);
            // Save to your backend
          },
        }}
        features={{
          timeCodes: [
            { fromMs: 0, description: "Introduction" },
            { fromMs: 130000, description: "Exciting Scene" },
            { fromMs: 270000, description: "Climax" },
          ],
          getPreviewScreenUrl: handleGettingPreview,
          tracking: {
            onViewed: () => console.log("Video Viewed"),
            onWatchTimeUpdated: (e) => {
              console.log("Watch Time Updated", {
                watchTime: e.watchTime,
                currentTime: e.currentTime,
                duration: e.duration,
              });
            },
          },
          subtitles: [
            {
              lang: "en",
              label: "English",
              url: "https://example.com/subtitles-en.vtt",
            },
            {
              lang: "hi",
              label: "Hindi",
              url: "https://example.com/subtitles-hi.vtt",
            },
          ],
          episodeList: [
            { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
            { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
          ],
          currentEpisodeIndex: 0,
          intro: { start: 5, end: 20 },
          nextEpisodeConfig: { showAtTime: 300, showAtEnd: true },
          ads: {
            preRoll: {
              id: "preroll-1",
              type: "pre-roll",
              time: 0,
              adUrl: "https://example.com/ad.mp4",
              skipable: true,
              skipAfter: 5,
            },
            midRoll: [
              {
                id: "midroll-1",
                type: "mid-roll",
                time: 120,
                adUrl: "https://example.com/mid-ad.mp4",
                skipable: false,
              },
            ],
            onAdStart: (adBreak) => console.log("Ad started", adBreak),
            onAdEnd: (adBreak) => console.log("Ad ended", adBreak),
          },
        }}
      />
    </div>
  );
}

export default App;

📚 Props Reference

The VideoPlayer component accepts four main prop objects:

video (Required)

Video source and basic configuration.

| Prop | Type | Default | Description | | -------------- | -------------------------------------------------- | ------------ | ------------------------------------------------------- | | src | string | Required | Video source URL (MP4, HLS, DASH, YouTube, etc.) | | title | string | "" | Title of the video displayed in the player header | | poster | string | "" | URL of the poster/thumbnail image shown before playback | | type | "hls" \| "dash" \| "mp4" \| "youtube" \| "other" | undefined | Video format type (auto-detected if not provided) | | isTrailer | boolean | false | If true, shows trailer-specific UI elements | | showControls | boolean | true | Show/hide player controls | | isMute | boolean | false | Start video muted | | startFrom | number | 0 | Start playback from specific time in seconds |

style (Optional)

Styling and appearance configuration.

| Prop | Type | Default | Description | | --------------- | --------------------- | ----------- | ---------------------------------------------- | | className | string | undefined | Custom CSS class name for the player container | | width | string | "100%" | Player width (e.g., "720px", "100%") | | height | string | "auto" | Player height (e.g., "405px", "auto") | | subtitleStyle | SubtitleStyleConfig | undefined | Custom styling for subtitles (see below) |

SubtitleStyleConfig:

| Prop | Type | Default | Description | | ----------------- | ------------------------------- | --------------------------------------------- | ---------------------------------- | | fontSize | string | "1.75rem" | Subtitle font size | | backgroundColor | string | "linear-gradient(135deg, #fbbf24, #f59e0b)" | Subtitle background color/gradient | | textColor | string | "#000000" | Subtitle text color | | position | "top" \| "center" \| "bottom" | "bottom" | Vertical position of subtitles | | borderRadius | string | "12px" | Border radius of subtitle box | | padding | string | "12px 20px" | Padding inside subtitle box | | maxWidth | string | "80%" | Maximum width of subtitle box |

events (Optional)

Event callbacks for player lifecycle.

| Prop | Type | Description | | ---------------------- | ------------------------------------------------------------- | -------------------------------------------------- | | onEnded | (e: React.SyntheticEvent<HTMLVideoElement>) => void | Called when video playback ends | | onError | (e?: React.SyntheticEvent<HTMLVideoElement, Event>) => void | Called when video encounters an error | | onClose | () => void | Called when player is closed | | onWatchHistoryUpdate | (data: WatchHistoryData) => void | Called when player closes with watch progress data |

WatchHistoryData:

{
  currentTime: number; // Current playback time in seconds
  duration: number; // Total video duration in seconds
  progress: number; // Progress percentage (0-100)
  isCompleted: boolean; // Whether video was fully watched
  watchedAt: number; // Timestamp when watch session ended
}

features (Optional)

Advanced features and functionality.

| Prop | Type | Default | Description | | --------------------- | ------------------------------------------------ | ----------- | ------------------------------------------------------------------------ | | timeCodes | Array<{ fromMs: number, description: string }> | [] | Time-based chapter markers (in milliseconds) | | getPreviewScreenUrl | (hoverTimeValue: number) => string | undefined | Function to generate preview thumbnail URLs while hovering over seek bar | | tracking | TrackingConfig | undefined | Event tracking configuration (see below) | | subtitles | Array<SubtitleTrack> | [] | Subtitle tracks in WebVTT format | | episodeList | Array<Episode> | [] | List of episodes for playlist/autoplay | | currentEpisodeIndex | number | 0 | Index of currently playing episode | | intro | { start: number, end: number } | undefined | Intro skip configuration (times in seconds) | | nextEpisodeConfig | { showAtTime?: number, showAtEnd?: boolean } | undefined | Configuration for next episode button | | ads | AdConfig | undefined | Advertisement configuration (see below) |

TrackingConfig:

{
  onViewed?: () => void;
  onWatchTimeUpdated?: (e: {
    watchTime?: number;    // Total watch time in seconds
    currentTime: number;   // Current playback position
    duration: number;      // Total video duration
  }) => void;
}

SubtitleTrack:

{
  lang: string; // Language code (e.g., "en", "hi", "fr")
  label: string; // Display label (e.g., "English", "Hindi")
  url: string; // URL to WebVTT subtitle file
}

Episode:

{
  id: number; // Unique episode identifier
  title: string; // Episode title
  url: string; // Episode video URL
}

AdConfig:

{
  preRoll?: AdBreak;                    // Pre-roll ad (plays before video)
  midRoll?: AdBreak[];                  // Mid-roll ads (plays during video)
  postRoll?: AdBreak;                   // Post-roll ad (plays after video)
  overlay?: {                           // Overlay ad (image overlay)
    imageUrl: string;
    clickUrl?: string;
    showDuration: number;
    position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
  };
  smartPlacement?: {                    // Smart ad placement
    enabled: boolean;
    minVideoDuration?: number;
    minGapBetweenAds?: number;
    avoidNearEnd?: number;
    preferNaturalBreaks?: boolean;
  };
  onAdStart?: (adBreak: AdBreak) => void;
  onAdEnd?: (adBreak: AdBreak) => void;
  onAdSkip?: (adBreak: AdBreak) => void;
  onAdError?: (adBreak: AdBreak, error: Error) => void;
}

AdBreak:

{
  id: string;                    // Unique ad identifier
  type: "pre-roll" | "mid-roll" | "post-roll" | "overlay";
  time: number;                  // Time in seconds when ad should play
  adUrl: string;                 // URL to ad video
  skipable?: boolean;            // Whether ad can be skipped
  skipAfter?: number;            // Seconds before skip button appears
  duration?: number;             // Ad duration in seconds
  sponsoredUrl?: string;         // URL for sponsored content
  title?: string;                // Ad title
  description?: string;          // Ad description
  relevance?: "high" | "medium" | "low";
}

🎯 Usage Examples

Basic Video Player

<VideoPlayer
  video={{
    src: "https://example.com/video.mp4",
    title: "My Video",
  }}
/>

HLS Video with Poster

<VideoPlayer
  video={{
    src: "https://example.com/playlist.m3u8",
    type: "hls",
    poster: "https://example.com/poster.jpg",
    title: "Streaming Video",
  }}
  style={{
    width: "100%",
    height: "450px",
  }}
/>

Video with Preview Thumbnails

const getPreview = (hoverTime: number) => {
  // Generate thumbnail URL based on hover time
  return `https://example.com/thumbnails/${Math.floor(hoverTime)}.jpg`;
};

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    getPreviewScreenUrl: getPreview,
  }}
/>;

Video with Chapters

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    timeCodes: [
      { fromMs: 0, description: "Introduction" },
      { fromMs: 60000, description: "Chapter 1" },
      { fromMs: 120000, description: "Chapter 2" },
      { fromMs: 180000, description: "Conclusion" },
    ],
  }}
/>

Video with Subtitles

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    subtitles: [
      {
        lang: "en",
        label: "English",
        url: "https://example.com/subtitles-en.vtt",
      },
      {
        lang: "es",
        label: "Spanish",
        url: "https://example.com/subtitles-es.vtt",
      },
    ],
  }}
  style={{
    subtitleStyle: {
      fontSize: "1.25rem",
      backgroundColor: "rgba(0, 0, 0, 0.75)",
      textColor: "#ffffff",
      position: "bottom",
    },
  }}
/>

Video with Intro Skip

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    intro: {
      start: 10, // Intro starts at 10 seconds
      end: 45, // Intro ends at 45 seconds
    },
  }}
/>

Episode Playlist

const [currentEpisode, setCurrentEpisode] = useState(0);

const episodes = [
  { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
  { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
  { id: 3, title: "Episode 3", url: "https://example.com/ep3.m3u8" },
];

<VideoPlayer
  video={{
    src: episodes[currentEpisode].url,
    title: episodes[currentEpisode].title,
  }}
  features={{
    episodeList: episodes,
    currentEpisodeIndex: currentEpisode,
    nextEpisodeConfig: {
      showAtTime: 300, // Show next episode button 5 minutes before end
      showAtEnd: true, // Also show at video end
    },
  }}
  events={{
    onEnded: () => {
      if (currentEpisode < episodes.length - 1) {
        setCurrentEpisode(currentEpisode + 1);
      }
    },
  }}
/>;

Video with Tracking

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    tracking: {
      onViewed: () => {
        // Track video view
        analytics.track("video_viewed", { videoId: "123" });
      },
      onWatchTimeUpdated: ({ watchTime, currentTime, duration }) => {
        // Track watch time
        analytics.track("watch_time_updated", {
          watchTime,
          progress: (currentTime / duration) * 100,
        });
      },
    },
  }}
/>

Video with Watch History

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  events={{
    onWatchHistoryUpdate: async (data) => {
      // Save watch history to backend
      await fetch("/api/watch-history", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          videoId: "123",
          currentTime: data.currentTime,
          duration: data.duration,
          progress: data.progress,
          isCompleted: data.isCompleted,
        }),
      });
    },
  }}
/>

Video with Ads

<VideoPlayer
  video={{ src: "https://example.com/video.mp4" }}
  features={{
    ads: {
      preRoll: {
        id: "preroll-1",
        type: "pre-roll",
        time: 0,
        adUrl: "https://example.com/pre-roll-ad.mp4",
        skipable: true,
        skipAfter: 5,
      },
      midRoll: [
        {
          id: "midroll-1",
          type: "mid-roll",
          time: 120,
          adUrl: "https://example.com/mid-roll-ad.mp4",
          skipable: false,
        },
      ],
      onAdStart: (adBreak) => {
        console.log("Ad started:", adBreak.id);
      },
      onAdEnd: (adBreak) => {
        console.log("Ad ended:", adBreak.id);
      },
    },
  }}
/>

Resume from Last Position

const [watchHistory, setWatchHistory] = useState(null);

// Load watch history on mount
useEffect(() => {
  fetch("/api/watch-history/123")
    .then((res) => res.json())
    .then((data) => setWatchHistory(data));
}, []);

<VideoPlayer
  video={{
    src: "https://example.com/video.mp4",
    startFrom: watchHistory?.currentTime || 0,
  }}
  events={{
    onWatchHistoryUpdate: (data) => {
      // Save progress
      setWatchHistory(data);
    },
  }}
/>;

❓ Troubleshooting

Video Not Playing?

  • Check the video URL: Ensure video.src is a valid, accessible URL
  • Check video format: Verify the video format is supported (HLS, MP4, DASH, YouTube)
  • CORS issues: If using HLS or external sources, ensure CORS headers are properly configured
  • Specify video type: Try explicitly setting video.type to help with format detection
<VideoPlayer
  video={{
    src: "https://example.com/video.m3u8",
    type: "hls", // Explicitly specify type
  }}
/>

Subtitles Not Showing?

  • Check VTT file URL: Ensure subtitle URLs are publicly accessible
  • Verify VTT format: Ensure WebVTT files are properly formatted
  • Check CORS: Subtitle files must be accessible from your domain
  • Test subtitle URL: Open the subtitle URL directly in a browser to verify it loads

Preview Thumbnails Not Loading?

  • Verify function returns URL: Ensure getPreviewScreenUrl returns a valid image URL
  • Check image loading: The function should return a URL that loads successfully
  • Use browser console: Check for image loading errors in the browser console (F12)

Player Not Responsive?

  • Use percentage widths: Set style.width to "100%" for responsive behavior
  • Container styling: Wrap the player in a container with appropriate CSS
  • Height auto: Use style.height: "auto" to maintain aspect ratio
<div style={{ maxWidth: "1200px", margin: "0 auto" }}>
  <VideoPlayer
    video={{ src: "https://example.com/video.mp4" }}
    style={{
      width: "100%",
      height: "auto",
    }}
  />
</div>

Ads Not Playing?

  • Check ad URLs: Ensure ad video URLs are valid and accessible
  • Verify ad timing: For mid-roll ads, ensure time is less than video duration
  • Check ad format: Ad videos should be in supported formats (MP4, HLS)

🔗 Related Links


📝 License

Licensed under the MIT License.
Developed by Zezosoft. 🚀


🙌 Credits

This project includes modifications of the seek bar functionality inspired by react-video-seek-slider.


🌟 Enjoy seamless video playback with @zezosoft/react-player! 🎥