> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JS Video Player & SDK

export const GcorePlayerExample = ({source}) => {
  const resolvedSource = source || "https://demo-public.gvideo.io/videos/2675_w6nGXEimHz4Z6t1j/master.m3u8";
  const reactId = React.useId();
  const containerRef = React.useRef(null);
  const playerIdRef = React.useRef(`gcore-player-${reactId.replace(/[:]/g, "-")}`);
  React.useEffect(() => {
    if (!containerRef.current) {
      return;
    }
    const playerCssUrl = "https://player.gvideo.co/v2/assets/latest/index.css";
    const playerModuleUrl = "https://player.gvideo.co/v2/assets/latest/index.js";
    const selectionResetStyleId = "gcore-player-selection-reset";
    if (!document.querySelector(`link[href="${playerCssUrl}"]`)) {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = playerCssUrl;
      document.head.appendChild(link);
    }
    if (!document.getElementById(selectionResetStyleId)) {
      const style = document.createElement("style");
      style.id = selectionResetStyleId;
      style.textContent = `
        body, body * {
          -webkit-user-select: text !important;
          user-select: text !important;
          -webkit-touch-callout: default !important;
        }

        #${playerIdRef.current}, #${playerIdRef.current} * {
          -webkit-user-select: none !important;
          user-select: none !important;
          -webkit-touch-callout: none !important;
        }
      `;
      document.head.appendChild(style);
    }
    const playerId = playerIdRef.current;
    const script = document.createElement("script");
    script.type = "module";
    script.dataset.gcorePlayerExample = playerId;
    script.textContent = `
      import {
        BigMuteButton,
        BottomGear,
        ErrorScreen,
        MediaControl,
        Player,
        QualityLevels,
        SourceController,
        Spinner,
      } from "${playerModuleUrl}";

      const container = document.getElementById(${JSON.stringify(playerId)});

      if (container) {
        window.__gcoreMintlifyPlayers = window.__gcoreMintlifyPlayers || {};

        if (!window.__gcoreMintlifyPlayerPluginsRegistered) {
          Player.registerPlugin(MediaControl);
          Player.registerPlugin(SourceController);
          Player.registerPlugin(Spinner);
          Player.registerPlugin(ErrorScreen);
          Player.registerPlugin(BigMuteButton);
          Player.registerPlugin(BottomGear);
          Player.registerPlugin(QualityLevels);
          window.__gcoreMintlifyPlayerPluginsRegistered = true;
        }

        const existingPlayer = window.__gcoreMintlifyPlayers[${JSON.stringify(playerId)}];
        if (existingPlayer?.destroy) {
          existingPlayer.destroy();
        }

        const player = new Player({
          autoPlay: true,
          mute: true,
          sources: [${JSON.stringify(resolvedSource)}],
          spinner: {
            showOnError: true,
            showOnStart: true,
          },
        });

        player.attachTo(container);
        window.__gcoreMintlifyPlayers[${JSON.stringify(playerId)}] = player;
      }
    `;
    document.body.appendChild(script);
    return () => {
      const player = window.__gcoreMintlifyPlayers?.[playerId];
      if (player?.destroy) {
        player.destroy();
      }
      if (window.__gcoreMintlifyPlayers) {
        delete window.__gcoreMintlifyPlayers[playerId];
      }
      script.remove();
      if (containerRef.current) {
        containerRef.current.innerHTML = "";
      }
    };
  }, [resolvedSource]);
  return <div className="not-prose my-6">
      <div className="overflow-hidden rounded-2xl border border-zinc-950/10 dark:border-white/10" style={{
    background: "linear-gradient(180deg, rgba(17, 24, 39, 0.04), rgba(17, 24, 39, 0.08))"
  }}>
        <div id={playerIdRef.current} ref={containerRef} className="w-full" style={{
    aspectRatio: "16 / 9",
    minHeight: "320px"
  }} />
      </div>
    </div>;
};

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="quickstart" label="Quick start">
    <p>Gcore JS Video Player (`@gcorevideo/player`) is a lightweight, customizable video player built on top of hls.js, dash.js, and the native `<video />` element. It supports LIVE and VOD playback with HLS, LL-HLS, MPEG-DASH, LL-DASH, and MP4, and works with any source URL, including non-Gcore sources.</p>

    <GcorePlayerExample />

    ## Install

    ### Install via npm or yarn

    <p>The Gcore Player SDK works with React, Vue, Svelte, plain JS, or CMS platforms like WordPress and Tilda — no framework dependency.</p>

    <Steps>
      <Step title="Install the SDK">
        Install the player package:

        ```
        npm install @gcorevideo/player
        ```

        or with yarn:

        ```
        yarn add @gcorevideo/player
        ```
      </Step>

      <Step title="Import and configure the player">
        Import the player styles and core modules, register the plugins, then initialize the player against the container element:

        ```js theme={null}
        import '@gcorevideo/player/dist/index.css'
        import { Player, MediaControl, SourceController } from '@gcorevideo/player'

        Player.registerPlugin(MediaControl)
        Player.registerPlugin(SourceController)

        const player = new Player({
          sources: ["https://example.com/your-video.m3u8"]
        })

        player.attachTo(document.getElementById('container'))
        ```
      </Step>

      <Step title="Add the container element">
        Define the container in HTML or the component template:

        ```html theme={null}
        <div id="container" style="width: 640px; height: 360px;"></div>
        ```

        Set the container width and height explicitly. The player attaches only if the container has visible dimensions.
      </Step>
    </Steps>

    ### Vanilla JS

    <p>As an alternative, load the player directly from a CDN without installation.</p>

    <p>Add all modules:</p>

    ```html theme={null}
    <script src="https://player.gvideo.co/v2/assets/latest/index.js"></script>
    ```

    <p>Or import only the modules and plugins needed:</p>

    ```html theme={null}
    <script type="module">
      import {
        Player,
        ...
      } from 'https://player.gvideo.co/v2/assets/latest/index.js'
      ...
    </script>
    ```

    <p>The [README](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/README.md) on GitHub documents all available exports and usage.</p>

    ### Version selection

    <p>Use `/latest/` to load the current release automatically. Specify a version number to keep the loaded version fixed until updated. Available versions are listed on [npmjs.com](https://www.npmjs.com/package/@gcorevideo/player?activeTab=versions).</p>

    ```html theme={null}
    <!-- Always current: -->
    <script src="https://player.gvideo.co/v2/assets/latest/index.js"></script>

    <!-- Fixed version: -->
    <script src="https://player.gvideo.co/v2/assets/{version}/index.js"></script>
    ```

    ## Set video source

    <p>The player accepts any HLS or MPEG-DASH URL for LIVE or VOD playback and determines the format automatically.</p>

    <p>**Single source:**</p>

    <p>LIVE ([live example](https://player.gvideo.co/streams/2675_19146)):</p>

    ```js theme={null}
    const player = new Player({
      sources: ["https://demo.gvideo.io/cmaf/2675_19146/master.m3u8"],
    })
    ```

    <p>VOD ([VOD example](https://player.gvideo.co/videos/2675_w6nGXEimHz4Z6t1j)):</p>

    ```js theme={null}
    const player = new Player({
      sources: ["https://demo-public.gvideo.io/videos/2675_w6nGXEimHz4Z6t1j/master.m3u8"],
    })
    ```

    <p>**Multiple sources with fallback:**</p>

    <p>Configure DASH as the primary transport with HLS as a fallback. If DASH is unsupported or fails, the player falls back to HLS. A poster image (from the `Poster` plugin `poster.url` setting) is shown before playback starts.</p>

    <p>LIVE ([live example](https://player.gvideo.co/streams/2675_19146)):</p>

    ```js theme={null}
    const player = new Player({
      autoPlay: true,
      mute: true,
      playbackType: "live",
      priorityTransport: 'dash', // [dash|hls], "dash" is default value
      sources: [
        {
          source: "https://demo-public.gvideo.io/cmaf/2675_19146/index.mpd",
          mimeType: 'application/dash+xml',
        },
        {
          source: "https://demo-public.gvideo.io/cmaf/2675_19146/master.m3u8",
          mimeType: 'application/x-mpegURL',
        }
      ],
      poster: { url: 'https://static.gvideo.co/videoplatform/posters/broadcast/21606/e0a5243fdd2ae36061b8cadfa5089fc8.jpeg' },
    })
    ```

    <p>VOD ([VOD example](https://player.gvideo.co/videos/2675_w6nGXEimHz4Z6t1j)):</p>

    ```js theme={null}
    const player = new Player({
      autoPlay: true,
      mute: true,
      playbackType: "vod",
      priorityTransport: 'dash', // [dash|hls], "dash" is default value
      sources: [
        {
          source: "https://demo-public.gvideo.io/videos/2675_w6nGXEimHz4Z6t1j/master.mpd",
          mimeType: 'application/dash+xml',
        },
        {
          source: "https://demo-public.gvideo.io/videos/2675_w6nGXEimHz4Z6t1j/master.m3u8",
          mimeType: 'application/x-mpegURL',
        }
      ],
      poster: { url: 'https://static.gvideo.co/videoplatform/posters/video/11452407/eb4fba797fc1f41309e5b0b552319208.jpeg' },
    })
    ```

    ## Configure playback

    <p>`autoPlay: true` requires `mute: true` to meet browser autoplay policies. Muted autoplay is generally permitted:</p>

    ```js theme={null}
    const player = new Player({
      autoPlay: true,           // start playback automatically
      mute: true,               // required with autoPlay for browser compatibility
      loop: true,               // repeat continuously (VOD only)
      sources: ["https://demo-public.gvideo.io/videos/2675_mvsPGvDVx0Hzbog/master.m3u8"],
      poster: {
        url: "https://static.gvideo.co/videoplatform/posters/video/11452143/6423b07877c27c372b205aa99fd13f42.jpeg",
        showForNoOp: true       // show poster when there is no active operation
      }
    })
    ```

    ## Customize the player

    ### Custom skin

    <p>Load the default stylesheet as a starting point:</p>

    ```html theme={null}
    <link rel="stylesheet" href="https://player.gvideo.co/v2/assets/latest/index.css" />
    ```

    <p>Most interface elements expose class names that can be targeted directly. Override styles in a scoped or external stylesheet to customize colors, icons, and layout.</p>

    ### Hide UI components

    <p>The player interface is modular. Visual elements are enabled through plugins. The `Media controls` plugin handles the core of the UI — if disabled, most visible components do not appear. Each UI-related plugin can be toggled dynamically at runtime:</p>

    ```js theme={null}
    player.on('ready', () => {
      const mc = player.getPlugin('MediaControl')
      if (mc) {
        mc.disable()
      }
    })
    ```

    <p>For a completely custom UI, disable the default controls via plugin settings and implement the interface on top of the [core playback API](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/docs/api/player.md).</p>

    ## Live demos

    <p>Two publicly hosted projects demonstrate the player in practice:</p>

    * **Vanilla JS** — dual Live/VOD player with source switching, custom external controls, and event logging. [Vanilla JS demo](https://g-core.github.io/gcore-videoplayer-js/example/)
    * **Nuxt** — framework integration with configurable plugins and sources. [Nuxt demo](https://gcore-videoplayer-js-nuxt.vercel.app/settings)

    <Frame>
      <img src="https://mintcdn.com/gcore/TvpiyW8JU3adlhQ3/images/docs/streaming/player/player-api-tutorial/player-js-demo.png?fit=max&auto=format&n=TvpiyW8JU3adlhQ3&q=85&s=12b9292c54e29b2239d338bb3e0cdc76" alt="Vanilla JS demo showing dual Live and VOD player panels with source switching and event log" width="1920" height="842" data-path="images/docs/streaming/player/player-api-tutorial/player-js-demo.png" />
    </Frame>
  </MethodSection>

  <MethodSection id="sdk" label="SDK">
    ## Add plugins

    <p>Plugins extend the player with additional capabilities. Register plugins before initialization:</p>

    ```js theme={null}
    import { Player, MediaControl, SourceController, QualityLevels } from '@gcorevideo/player'

    Player.registerPlugin(MediaControl)      // core UI controls
    Player.registerPlugin(SourceController)  // automatic source selection
    Player.registerPlugin(QualityLevels)    // manual quality selection
    ```

    <p>The [Plugin reference](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/docs/api/player.md) on GitHub documents all available plugins and their configuration.</p>

    ## Control playback

    <p>Instance methods control playback after the player is initialized:</p>

    | Category     | Method                    | Description                                     |
    | ------------ | ------------------------- | ----------------------------------------------- |
    | **Playback** | `player.play()`           | Start playback                                  |
    |              | `player.pause()`          | Pause playback                                  |
    |              | `player.stop()`           | Stop and clear the current stream               |
    | **Volume**   | `player.mute()`           | Mute audio                                      |
    |              | `player.unmute()`         | Unmute audio                                    |
    |              | `player.setVolume(0.5)`   | Set volume (0.0–1.0)                            |
    |              | `player.isMuted()`        | Check if muted                                  |
    |              | `player.getVolume()`      | Get current volume                              |
    | **Seek**     | `player.getCurrentTime()` | Get current position (seconds)                  |
    |              | `player.getDuration()`    | Get total duration (seconds)                    |
    |              | `player.seek(120)`        | Jump to position (seconds)                      |
    | **Cleanup**  | `player.destroy()`        | Release resources before removing the container |

    <p>The [Player class reference](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/docs/api/player.player.md#methods) on GitHub lists all methods.</p>

    ## Track events

    <p>The player emits events throughout the playback lifecycle. Attach handlers with `player.on()`:</p>

    ```js theme={null}
    player.on("play", () => console.log("Playback started"))
    player.on("pause", () => console.log("Paused"))
    player.on("error", (error) => console.warn("Error:", error.message))
    ```

    | Category        | Event          | Description                                    |
    | --------------- | -------------- | ---------------------------------------------- |
    | **Playback**    | `pause`        | Triggered when playback is paused              |
    | **Seek & time** | `seek`         | Fired when the current position changes        |
    | **Audio**       | `volumeupdate` | Fired when volume or mute state changes        |
    | **Errors**      | `error`        | Fired when playback fails or media cannot load |

    <p>The [PlayerEvent reference](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/docs/api/player.md) on GitHub lists all events and parameters.</p>

    ## Handle errors

    <p>The `error` event returns a `PlaybackError` object with an error code, message, severity, and source:</p>

    ```js theme={null}
    player.on("error", (error) => {
      console.warn("Playback error:", error.message)
    })
    ```

    <p>Common causes:</p>

    * Invalid or unreachable video URLs
    * Missing or corrupted media metadata
    * Unrecognized formats or codec issues

    <p>The SourceController plugin automates fallback logic and recovery scenarios. The [PlaybackError reference](https://github.com/G-Core/gcore-videoplayer-js/blob/main/packages/player/docs/api/player.playbackerror.md) on GitHub documents all error codes.</p>

    ## Debug

    <p>If the video does not start, buffers indefinitely, or behaves unexpectedly, enable logs to investigate. The `@gcorevideo/utils` package provides `Logger` (controls which log categories are captured) and `LogTracer` (writes them to the browser console):</p>

    ```js theme={null}
    import { Logger, LogTracer } from "@gcorevideo/utils"
    Logger.enable("*")                         // enable all log categories
    const tracer = new LogTracer("my-player") // named tracer for console output
    ```

    <p>The console output includes player lifecycle events, source selection, buffering status, and error details — useful for diagnosing unsupported formats or network failures.</p>

    <p>Gcore Player integrates with remote logging platforms like Sentry. The Nuxt example repository demonstrates [Sentry integration](https://github.com/dmitritz/gcore-videoplayer-js-nuxt/blob/29f9c6bb226970886962f99a9d475f57169bceba/app.vue#L56) and [server-side logging](https://github.com/dmitritz/gcore-videoplayer-js-nuxt/blob/29f9c6bb226970886962f99a9d475f57169bceba/app.vue#L38C22-L38C34).</p>

    ## Limits

    ### Views statistics

    <p>This player does not collect or send usage statistics from end user devices. Viewing statistics are available from CDN data via the [CDN statistics API](/api-reference/streaming/statistics/get-unique-viewers-via-cdn).</p>

    ### Feature requests

    <p>To request a missing feature or capability, contact [Gcore support](https://gcore.com/contact-us).</p>
  </MethodSection>
</MethodSwitch>
