Skip to main content
The @convertly-sh/player package is Convertly’s official embeddable player for HLS streams created through 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.

Live demo

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, Fullscreen, and the Live badge 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

npm install @convertly-sh/player

React

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

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

OptionDescription
manifestUrlHLS master playlist (.m3u8). Preferred for Convertly streams.
srcDirect progressive URL (MP4) when you are not using HLS.
posterUrlPoster image shown before playback.
playbackIdEnables analytics to /api/video/playback-events.
captionsWebVTT subtitle tracks (url, label, language).
previewVttUrlSpritesheet WebVTT for scrub-bar hover previews (CDN ?format=vtt).
chapters{ title, start }[] — markers on the timeline plus a chapters menu.
qualitiesManual renditions for MP4 ({ label, src }[]). HLS streams auto-detect levels from the manifest.
seekPreviewFrame preview on hover when no VTT is set. Default true.
brandingDefault bundled logomark; { logoUrl, href, logoAlt } to override, or false to hide.
analyticsDefaults to true when playbackId is set.
liveLive-stream mode. Hides seeking, skip buttons, and playback speed while keeping quality selection available. The top Live badge auto-hides with the controls.
liveLabelBottom-bar label for live streams. Defaults to Channel Name.
liveBadgePersistentKeeps the top-right Live badge visible even after the controls auto-hide.

Examples

Manual MP4 qualities

<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

<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;
  }}
/>

Live stream presentation

<ConvertlyVideo
  manifestUrl={liveStream.hlsManifestUrl}
  autoplay
  muted
  live
  liveLabel="Channel Name"
/>

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 hover, similar to Cloudinary. 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.
// 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.

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.