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

# Introduction

> Composable, minimal custom elements for the BeyondWords player. Own the code and style it your way.

export const WebComponentsDemo = ({name}) => {
  const MOCK_CONTENT = [{
    id: "demo-1",
    title: "How AI voices are changing publishing",
    imageUrl: "/images/player-ui/demo-cover.svg",
    sourceUrl: "https://beyondwords.io",
    sourceId: "demo",
    adsEnabled: false,
    duration: 124,
    audio: [{
      id: "audio-1",
      url: "/images/player-ui/demo-silent.mp3",
      contentType: "audio/mpeg",
      duration: 124
    }],
    video: [],
    summarization: {
      audio: [],
      video: []
    },
    segments: []
  }];
  const MOCK_PLAYLIST = [{
    id: "7a974fb1-1b89-4be3-a25d-4ad286d5305b",
    title: "Part 1: The newsroom shift",
    imageUrl: "/images/player-ui/demo-cover.svg",
    sourceUrl: "https://beyondwords.io",
    sourceId: "demo",
    adsEnabled: false,
    duration: 124,
    audio: [{
      id: "audio-1",
      url: "/images/player-ui/demo-silent.mp3",
      contentType: "audio/mpeg",
      duration: 124
    }],
    video: [],
    summarization: {
      audio: [],
      video: []
    },
    segments: []
  }, {
    id: "129e7681-fa4d-4526-8be9-d27c946a5023",
    title: "Part 2: Voices at scale",
    imageUrl: "/images/player-ui/demo-cover.svg",
    sourceUrl: "https://beyondwords.io",
    sourceId: "demo",
    adsEnabled: false,
    duration: 98,
    audio: [{
      id: "audio-1",
      url: "/images/player-ui/demo-silent.mp3",
      contentType: "audio/mpeg",
      duration: 98
    }],
    video: [],
    summarization: {
      audio: [],
      video: []
    },
    segments: []
  }, {
    id: "e9f2257c-14cf-46c0-bab6-bcaa9e229775",
    title: "Part 3: What comes next",
    imageUrl: "/images/player-ui/demo-cover.svg",
    sourceUrl: "https://beyondwords.io",
    sourceId: "demo",
    adsEnabled: false,
    duration: 152,
    audio: [{
      id: "audio-1",
      url: "/images/player-ui/demo-silent.mp3",
      contentType: "audio/mpeg",
      duration: 152
    }],
    video: [],
    summarization: {
      audio: [],
      video: []
    },
    segments: []
  }];
  const MOCK_ADVERTS = [{
    id: "advert-1",
    type: "native",
    placement: "pre-roll",
    vastUrl: null,
    clickThroughUrl: "https://sponsor.example.com",
    imageUrl: "/images/player-ui/demo-cover.svg",
    textColor: "#ffffff",
    backgroundColor: "#943bfc",
    iconColor: "#ffffff",
    audio: [{
      id: "advert-audio-1",
      url: "/images/player-ui/demo-silent.mp3",
      contentType: "audio/mpeg",
      duration: 8
    }],
    video: []
  }];
  const content = JSON.stringify(MOCK_CONTENT).replace(/'/g, "&#39;");
  const playlistContent = JSON.stringify(MOCK_PLAYLIST).replace(/'/g, "&#39;");
  const adverts = JSON.stringify(MOCK_ADVERTS).replace(/'/g, "&#39;");
  const DEMOS = {
    "play-pause-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-play-pause-button></bw-play-pause-button></bw-player-provider>`,
    "time-indicator": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-time-indicator></bw-time-indicator></bw-player-provider>`,
    "prev-segment-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-prev-segment-button></bw-prev-segment-button></bw-player-provider>`,
    "next-segment-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-next-segment-button></bw-next-segment-button></bw-player-provider>`,
    "prev-seconds-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-prev-seconds-button></bw-prev-seconds-button></bw-player-provider>`,
    "next-seconds-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-next-seconds-button></bw-next-seconds-button></bw-player-provider>`,
    "playback-rate-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-playback-rate-button></bw-playback-rate-button></bw-player-provider>`,
    "content-title": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-content-title></bw-content-title></bw-player-provider>`,
    "call-to-action": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-call-to-action></bw-call-to-action></bw-player-provider>`,
    "download-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-download-button></bw-download-button></bw-player-provider>`,
    "beyondwords-logo": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><bw-beyondwords-logo></bw-beyondwords-logo></bw-player-provider>`,
    "prev-track-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${playlistContent}'><bw-prev-track-button></bw-prev-track-button></bw-player-provider>`,
    "next-track-button": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${playlistContent}'><bw-next-track-button></bw-next-track-button></bw-player-provider>`,
    "playlist": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${playlistContent}'><bw-playlist></bw-playlist></bw-player-provider>`,
    "feature-image": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><div style="width:100%;max-width:20rem;min-width:15rem;overflow:hidden;border-radius:0.5rem"><bw-feature-image></bw-feature-image></div></bw-player-provider>`,
    "progress-bar": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}'><div style="display:flex;flex-direction:row;align-items:center;min-width:30rem"><bw-progress-bar></bw-progress-bar></div></bw-player-provider>`,
    "advert-link": `<bw-player-provider data-show-user-interface="false" data-analytics-consent="none" data-content='${content}' data-adverts='${adverts}' data-advert-index="0"><bw-advert-link></bw-advert-link></bw-player-provider>`,
    "standard-player": `<bw-standard-player-provider data-show-user-interface="false" data-analytics-consent="none" data-project-id="9504" data-content-id="ba00ef51-03ac-4a85-8b98-e45c957e8ef8"><bw-standard-player-bar><bw-standard-play-pause-button></bw-standard-play-pause-button><bw-standard-player-info><bw-standard-call-to-action></bw-standard-call-to-action><bw-standard-duration></bw-standard-duration></bw-standard-player-info><bw-standard-playback-controls><bw-standard-playback-rate-button></bw-standard-playback-rate-button><bw-standard-prev-segment-button></bw-standard-prev-segment-button><bw-standard-next-segment-button></bw-standard-next-segment-button><bw-standard-time-indicator></bw-standard-time-indicator><bw-standard-progress-bar></bw-standard-progress-bar><bw-standard-close-widget-button></bw-standard-close-widget-button></bw-standard-playback-controls><bw-standard-beyondwords-logo></bw-standard-beyondwords-logo></bw-standard-player-bar></bw-standard-player-provider>`,
    "minimal-player": `<bw-minimal-player-provider data-show-user-interface="false" data-analytics-consent="none" data-project-id="9504" data-content-id="ba00ef51-03ac-4a85-8b98-e45c957e8ef8"><bw-minimal-player-bar><bw-minimal-play-pause-button></bw-minimal-play-pause-button><bw-minimal-call-to-action></bw-minimal-call-to-action><bw-minimal-prev-seconds-button data-seconds="10"></bw-minimal-prev-seconds-button><bw-minimal-playback-rate-menu></bw-minimal-playback-rate-menu><bw-minimal-current-time></bw-minimal-current-time></bw-minimal-player-bar></bw-minimal-player-provider>`,
    "playlist-player": `<bw-playlist-player-provider data-show-user-interface="false" data-analytics-consent="none" data-project-id="9504" data-playlist-id="153102"><bw-playlist-player-root><bw-playlist-player-bar><bw-playlist-play-pause-button></bw-playlist-play-pause-button><bw-playlist-player-info><bw-playlist-call-to-action></bw-playlist-call-to-action><bw-playlist-duration></bw-playlist-duration></bw-playlist-player-info><bw-playlist-playback-controls><bw-playlist-playback-rate-button></bw-playlist-playback-rate-button><bw-playlist-prev-track-button></bw-playlist-prev-track-button><bw-playlist-next-track-button></bw-playlist-next-track-button><bw-playlist-time-indicator></bw-playlist-time-indicator><bw-playlist-progress-bar></bw-playlist-progress-bar></bw-playlist-playback-controls><bw-playlist-toggle-button></bw-playlist-toggle-button></bw-playlist-player-bar><bw-playlist-list></bw-playlist-list></bw-playlist-player-root></bw-playlist-player-provider>`,
    "video-player": `<bw-video-player-provider data-show-user-interface="false" data-analytics-consent="none" data-video="true" data-project-id="54009" data-content-id="5ab6a5e1-689c-4103-962d-e7e68a4258ff"><bw-video-feature-image></bw-video-feature-image><bw-video-control-bar><bw-video-prev-seconds-button data-color="white" data-seconds="10"></bw-video-prev-seconds-button><bw-video-play-pause-button data-color="white" data-size="56"></bw-video-play-pause-button><bw-video-next-seconds-button data-color="white" data-seconds="10"></bw-video-next-seconds-button></bw-video-control-bar><bw-video-widget-bar><bw-video-play-pause-button></bw-video-play-pause-button><bw-video-widget-controls><bw-video-playback-rate-button></bw-video-playback-rate-button><bw-video-prev-seconds-button data-seconds="10"></bw-video-prev-seconds-button><bw-video-next-seconds-button data-seconds="10"></bw-video-next-seconds-button><bw-video-time-indicator></bw-video-time-indicator><bw-video-progress-bar></bw-video-progress-bar><bw-video-close-widget-button></bw-video-close-widget-button></bw-video-widget-controls></bw-video-widget-bar></bw-video-player-provider>`,
    "vertical-video-player": `<bw-video-player-provider data-show-user-interface="false" data-analytics-consent="none" data-video="true" data-aspect="9/16" data-height="480" data-project-id="54009" data-content-id="5ab6a5e1-689c-4103-962d-e7e68a4258ff"><bw-video-feature-image></bw-video-feature-image><bw-video-control-bar><bw-video-prev-seconds-button data-color="white" data-seconds="10"></bw-video-prev-seconds-button><bw-video-play-pause-button data-color="white" data-size="56"></bw-video-play-pause-button><bw-video-next-seconds-button data-color="white" data-seconds="10"></bw-video-next-seconds-button></bw-video-control-bar><bw-video-widget-bar><bw-video-play-pause-button></bw-video-play-pause-button><bw-video-widget-controls><bw-video-playback-rate-button></bw-video-playback-rate-button><bw-video-prev-seconds-button data-seconds="10"></bw-video-prev-seconds-button><bw-video-next-seconds-button data-seconds="10"></bw-video-next-seconds-button><bw-video-time-indicator></bw-video-time-indicator><bw-video-progress-bar></bw-video-progress-bar><bw-video-close-widget-button></bw-video-close-widget-button></bw-video-widget-controls></bw-video-widget-bar></bw-video-player-provider>`
  };
  const markup = DEMOS[name];
  return <div className="not-prose flex w-full items-center justify-center rounded-lg border p-10" style={{
    minHeight: "16rem",
    fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
  }}>
      {markup ? <div dangerouslySetInnerHTML={{
    __html: markup
  }} /> : <p className="text-sm">No preview available for “{name}”.</p>}
    </div>;
};

A set of framework-agnostic [custom elements](https://developer.mozilla.org/docs/Web/Web_Components)
for building your own UI on top of the
[BeyondWords player](https://github.com/beyondwords-io/player). They give you
play/pause, seeking, playback rate and more as small building blocks you compose
in plain HTML and style yourself. The player keeps doing the hard part:
playback, analytics, ads, segments and lock-screen integration.

This is not a packaged widget. The elements are distributed as a
[shadcn registry](https://ui.shadcn.com/docs/registry), so **you own the code**:
the CLI copies the source straight into your project, where you can restyle,
rearrange or rewrite it.

<CodeGroup>
  ```bash npm theme={null}
  npx @beyondwords/cli components add play-pause-button --html
  ```

  ```bash pnpm theme={null}
  pnpm dlx @beyondwords/cli components add play-pause-button --html
  ```

  ```bash yarn theme={null}
  yarn dlx @beyondwords/cli components add play-pause-button --html
  ```

  ```bash bun theme={null}
  bunx --bun @beyondwords/cli components add play-pause-button --html
  ```
</CodeGroup>

Here's the [Standard Player](/player-ui/html/standard-player) example, running
live, built entirely from these elements:

<WebComponentsDemo name="standard-player" />

<Info>
  Using React? Everything here is also available as
  [React components](/player-ui/react/introduction). Use the React / HTML tabs
  at the top of the Components section.
</Info>

## How it works

The kit is three layers on top of one engine. Stop at whichever layer
gives you the control you need:

1. **[Player Provider](/player-ui/html/player-provider)**:
   `<bw-player-provider>` mounts the player JavaScript SDK (loaded as the
   global `BeyondWords`), holds the instance, and shares it with the elements
   inside. This is the foundation everything else sits on.
2. **Components**: small, minimal custom elements like
   [Play Pause Button](/player-ui/html/play-pause-button) and
   [Progress Bar](/player-ui/html/progress-bar). You bring the styling.
3. **Examples**: complete, pre-assembled players you drop in and tweak, like
   the [Standard Player](/player-ui/html/standard-player).

Every element behaves the same way:

* **It reads state from the player instance** by extending `BwPlayerConsumer`,
  which resolves the nearest provider and exposes the instance as
  `this._player`. Elements stay in sync by listening for player events and
  updating their own markup.
* **It drives the player by emitting events** built with
  [`newEvent`](/player-ui/html/new-event). This is the same path the standard
  player UI uses, so analytics, ads and segment logic stay correct. Simple
  state such as `playbackRate` can also be set directly on the instance.

Every layer is copied into your codebase, so there's no black box and no
version lock-in. The trade-off is that you maintain what you customize.

## Next steps

<CardGroup cols={2}>
  <Card title="Setup" href="/player-ui/html/setup">Install the elements and build your first player.</Card>
  <Card title="Components" href="/player-ui/html/play-pause-button">Browse the minimal building blocks.</Card>
  <Card title="Theming" href="/player-ui/html/theming">Restyle the elements to match your brand.</Card>
  <Card title="Examples" href="/player-ui/html/standard-player">Drop in a complete, pre-built player.</Card>
</CardGroup>
