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

# Video player

> Official Convertly video player with HLS playback, custom controls, analytics, and React component.

The `@convertly-sh/player` package is Convertly's official embeddable player for HLS streams created through [Video streaming](/docs/video-streaming). It wraps **hls.js** (with Safari native HLS fallback), ships styled controls, optional branding, and posts playback analytics when you pass a `playbackId`.

<Card title="npm package" icon="npm">
  **[@convertly-sh/player](https://www.npmjs.com/package/@convertly-sh/player)** — install with `npm install @convertly-sh/player`. Source: [github.com/convertly-sh/player](https://github.com/convertly-sh/player).
</Card>

## Interactive demo

<Frame caption="Interactive player — HLS adaptive streaming with Convertly controls.">
  <iframe src="https://convertly.sh/demo/player" title="Convertly video player demo" width="100%" height="480" scrolling="no" style={{ border: 0, borderRadius: "10px", display: "block", overflow: "hidden" }} allow="autoplay; fullscreen; picture-in-picture; encrypted-media" />
</Frame>

The demo above is embedded with a Mintlify `<iframe>` inside a `<Frame>`. Mintlify supports raw HTML embeds, `<video>` tags for MP4 previews, and iframes for full interactive demos hosted on your app.

## Customizable by design

The player UI is built to be hot-swappable where product teams usually need control: theme tokens, branding, skip buttons, center controls, chapters, quality renditions, popovers, and overlay/control-bar slots can all be changed without forking the player. Native controls such as Settings, Quality, Chapters, Captions, PiP, and Fullscreen stay inside the player chrome so custom UI still feels like one cohesive player.

In the Settings menu, Quality is a nested player-native control. It shows the active rendition in the main settings view, then slides into the available quality list when selected. HLS streams populate this list from the manifest; MP4 workflows can pass manual `qualities`.

## Install

```bash theme={"system"}
npm install @convertly-sh/player
```

### Package exports

| Import                                            | Use for                                                             |
| ------------------------------------------------- | ------------------------------------------------------------------- |
| `@convertly-sh/player`                            | `mountConvertlyPlayer`, `bindPlaybackAnalytics`, `attachHls`, types |
| `@convertly-sh/player/react`                      | `ConvertlyVideo` React component                                    |
| `@convertly-sh/player/styles.css`                 | Player chrome and control styles (required)                         |
| `@convertly-sh/player/assets/logomark-cutout.svg` | Default branding asset                                              |

### React peer dependencies

The React entry is optional. When you use `@convertly-sh/player/react`, install **React 18+** and **react-dom** in your app:

```bash theme={"system"}
npm install @convertly-sh/player react react-dom
```

## React

```tsx theme={"system"}
import { ConvertlyVideo } from "@convertly-sh/player/react";
import "@convertly-sh/player/styles.css";

export function StreamPlayer({ stream }) {
  return (
    <ConvertlyVideo
      manifestUrl={stream.hlsManifestUrl}
      posterUrl={stream.posterUrl}
      playbackId={stream.playbackId}
      captions={stream.captions}
      previewVttUrl={stream.previewVttUrl}
      chapters={stream.chapters}
    />
  );
}
```

## Vanilla JS

```ts theme={"system"}
import { mountConvertlyPlayer } from "@convertly-sh/player";
import "@convertly-sh/player/styles.css";

const player = mountConvertlyPlayer(document.getElementById("player")!, {
  manifestUrl: stream.hlsManifestUrl,
  posterUrl: stream.posterUrl,
  playbackId: stream.playbackId,
});

// Call player.destroy() when removing the element.
```

## Options

| Option                                 | Description                                                                                              |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `manifestUrl`                          | HLS master playlist (`.m3u8`). Preferred for Convertly streams.                                          |
| `src`                                  | Direct progressive URL (MP4) when you are not using HLS.                                                 |
| `posterUrl`                            | Poster image shown before playback.                                                                      |
| `playbackId`                           | Enables analytics to `/api/video/playback-events`.                                                       |
| `captions`                             | WebVTT subtitle tracks (`url`, `label`, `language`, optional inline `vtt`).                              |
| `previewVttUrl`                        | Spritesheet WebVTT for scrub-bar hover previews ([CDN `?format=vtt`](/docs/image-cdn/transforms/video)). |
| `previewSpriteUrl`                     | JPEG spritesheet paired with `previewVttUrl` (derived from VTT URL when omitted).                        |
| `chapters`                             | `{ title, start }[]` — markers on the timeline plus a chapters menu.                                     |
| `chapterPositions`                     | Segmented progress bar per chapter. Default `false`. Requires `chapters`.                                |
| `positions`                            | Key-moment ticks on the progress bar (`{ title?, time }[]`).                                             |
| `qualities`                            | Manual renditions for MP4 (`{ label, src }[]`). HLS streams auto-detect levels from the manifest.        |
| `skip` / `centerSkip`                  | Back/forward skip buttons in the control bar or center cluster (`5`–`60` seconds, or `false`).           |
| `centerPlay`                           | Large center play/pause button. Default `true`.                                                          |
| `seekPreview`                          | Frame preview on hover when no VTT is set. Default `true`.                                               |
| `clipRange`                            | `[start, end]` in seconds — constrain playback and seeking.                                              |
| `branding`                             | Default bundled logomark; `{ logoUrl, href, logoAlt }` to override, or `false` to hide.                  |
| `theme`                                | `accentColor`, buffer/track colors, `fontFamily`, `controlsHideDelayMs`.                                 |
| `slots` / React `overlay` props        | Custom DOM in overlay, `controlsStart`, or `controlsEnd`.                                                |
| `popovers`                             | Extra control-bar menus (trigger + list or custom panel).                                                |
| `title`                                | Title overlay on the player chrome.                                                                      |
| `autoplay`, `muted`, `loop`, `preload` | Standard `<video>` behavior.                                                                             |
| `controls`                             | Show bottom control bar. Default `true`.                                                                 |
| `pip`, `download`                      | Picture-in-picture and download controls.                                                                |
| `disableSeek`                          | Disable scrubbing and keyboard seek.                                                                     |
| `resumePlayback`                       | Persist and restore position in `localStorage`.                                                          |
| `analytics`                            | Defaults to `true` when `playbackId` is set.                                                             |
| `baseUrl`                              | API origin for analytics (defaults to current site).                                                     |
| `fetch`                                | Custom `fetch` for analytics or caption requests.                                                        |

The mount handle exposes `destroy()`, `play()`, `pause()`, `getVideo()`, `getSlot()`, `setCaptions()`, and `setClipRange()` for imperative control after mount.

## Examples

### Manual MP4 qualities

```tsx theme={"system"}
<ConvertlyVideo
  src="/videos/launch-1080p.mp4"
  qualities={[
    { label: "Auto", src: "/videos/launch-1080p.mp4" },
    { label: "1080p", src: "/videos/launch-1080p.mp4" },
    { label: "720p", src: "/videos/launch-720p.mp4" },
    { label: "480p", src: "/videos/launch-480p.mp4" },
  ]}
/>
```

### Custom player chrome

```tsx theme={"system"}
<ConvertlyVideo
  manifestUrl={stream.hlsManifestUrl}
  branding={{ logoUrl: "https://cdn.example.com/mark.svg", href: null }}
  centerSkip={{ backward: 10, forward: 10 }}
  theme={{
    accentColor: "#ffffff",
    controlsHideDelayMs: 2500,
  }}
  overlay={({ video }) => {
    const badge = document.createElement("button");
    badge.type = "button";
    badge.textContent = "Watch trailer";
    badge.onclick = () => void video.play();
    return badge;
  }}
/>
```

## Scrub preview

Pass `previewVttUrl` pointing at a Convertly CDN VTT file (`?format=vtt&w=160&cols=6&interval=2` on the same video). Each cue maps a timestamp to a `#xywh=` region on the spritesheet — the player shows a thumbnail and time label on scrub-bar hover.

Without a VTT, the player falls back to seek-based frame preview when `seekPreview` is enabled (works best for same-origin or CORS-enabled sources).

## Quality selection

When playback uses **hls.js**, a **Quality** menu appears with **Auto** plus each HLS rendition (1080p, 720p, etc.). Manual selection locks the level; choose **Auto** to return to adaptive bitrate. Native Safari HLS keeps adaptive-only behavior.

## Chapters

Pass `chapters` with `{ title, start }` entries (seconds). The player renders tick marks on the progress bar, shows the chapter title on hover, and exposes a chapters menu to jump between sections.

## Branding

The player ships with a bundled white **logomark cutout** (`assets/logomark-cutout.svg`) and shows it by default, linking to [convertly.sh](https://convertly.sh).

```tsx theme={"system"}
// Default — bundled logomark
<ConvertlyVideo manifestUrl={url} />

// Custom logo
<ConvertlyVideo manifestUrl={url} branding={{ logoUrl: "https://yoursite.com/logo.svg" }} />

// White-label — no logo
<ConvertlyVideo manifestUrl={url} branding={false} />
```

Override `logoUrl`, set `logoUrl: ""` to hide the image while keeping other branding options, or pass `href: null` to remove the link.

## Analytics

When `playbackId` is provided, the player sends the same lightweight events as the legacy `@convertly-sh/sdk` helper: `ready`, `play`, `pause`, `ended`, `seeked`, `timeupdate` (throttled), and `error`.

Use `bindPlaybackAnalytics(video, { playbackId, baseUrl })` from `@convertly-sh/player` if you attach analytics to a raw `<video>` element instead of the full player UI.

## Advanced HLS helpers

For custom players that only need adaptive streaming:

```ts theme={"system"}
import { attachHls, isHlsUrl } from "@convertly-sh/player";

const video = document.querySelector("video")!;
const cleanup = attachHls(video, manifestUrl);
// cleanup() when unmounting
```

`isHlsUrl()` detects `.m3u8` URLs before wiring hls.js.

## Signed playback

For signed streams, pass the full `hlsManifestUrl` returned by the API (including `expires` and `token`). Convertly rewrites playlist segment URLs so tokens propagate through adaptive renditions. Refresh the manifest URL before it expires.

## Related

* [Video streaming](/docs/video-streaming) — create streams and playback URLs
* [JavaScript SDK](/docs/sdk) — API client for stream management
