# createPlayer

Factory function that creates a typed player element and controller for HTML custom elements

## Import

```ts
import { createPlayer } from "@videojs/html";
import { videoFeatures } from "@videojs/html/video";
```

`createPlayer` is the entry point for custom HTML player compositions. It accepts a `features` array and returns the configured `PlayerElement`, a [`PlayerController`](https://videojs.org/docs/framework/html/reference/api/player-controller) already bound to that player, and `playerContext` as a lower-level escape hatch. Import `ContainerElement` separately when you need a custom container.

**player.ts**

```ts
import { createPlayer, UIElement, selectPlayback } from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';

const { PlayerElement: VideoPlayerElement, PlayerController } = createPlayer({
  features: videoFeatures,
});

customElements.define('video-player', VideoPlayerElement);

// Control element with selector
class PlayButton extends UIElement {
  #playback = new PlayerController(this, selectPlayback);
}
```

`PlayerElement` and `PlayerController` are scoped to the created player’s feature set. The element owns its store and attachment lifecycle; the configured controller does not need a context argument. `ContainerElement` consumes the shared player context, so the same class works with any created player.

Use split elements (recommended for reusable skins/layouts):

**player.ts**

```ts
import { ContainerElement, createPlayer } from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';

const { PlayerElement: PlayerRoot } = createPlayer({ features: videoFeatures });

class PlayerRegion extends ContainerElement {}
```

## Examples

### Basic Usage

**index.html**

```html
<demo-video-player class="demo-video-player">
  <media-container>
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoplay muted playsinline></video>
    <demo-play-toggle class="media-play-button">
      <span class="paused">Play</span>
      <span class="playing">Pause</span>
    </demo-play-toggle>
  </media-container>
</demo-video-player>
```

**index.css**

```css
.demo-video-player media-container {
  position: relative;
}

.demo-video-player media-container video {
  width: 100%;
}

.media-play-button {
  position: absolute;
  bottom: 10px;
  left: 10px;
  padding-block: 8px;
  padding-inline: 20px;
  color: black;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 9999px;
  backdrop-filter: blur(10px);
}

.media-play-button .paused {
  display: none;
}

.media-play-button .playing {
  display: none;
}

.media-play-button[data-paused] .paused {
  display: inline;
}

.media-play-button:not([data-paused]) .playing {
  display: inline;
}
```

**index.ts**

```ts
import {
  applyElementProps,
  applyStateDataAttrs,
  createButton,
  createPlayer,
  selectPlayback,
  UIElement,
} from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';
import '@videojs/html/ui/container';

const { PlayerElement: VideoPlayerElement, PlayerController } = createPlayer({
  features: videoFeatures,
});

class PlayToggle extends UIElement {
  static readonly tagName = 'demo-play-toggle';

  readonly #player = new PlayerController(this, selectPlayback);

  #disconnect: AbortController | null = null;

  override connectedCallback(): void {
    super.connectedCallback();
    this.#disconnect = new AbortController();

    const buttonProps = createButton({
      onActivate: () => {
        const state = this.#player.value;
        if (!state) return;

        state.paused ? state.play() : state.pause();
      },
      isDisabled: () => !this.#player.value,
    });

    applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();
    this.#disconnect?.abort();
    this.#disconnect = null;
  }

  protected override update(changed: Map<string, unknown>): void {
    super.update(changed);
    const state = this.#player.value;
    if (!state) return;

    applyStateDataAttrs(this, state, { paused: 'data-paused', ended: 'data-ended' });
  }
}

customElements.define('demo-video-player', VideoPlayerElement);
customElements.define(PlayToggle.tagName, PlayToggle);
```

## API Reference

### Video

`createPlayer(config): CreatePlayerResult<VideoPlayerStore>`

Creates a typed HTML player class and bound controller.

#### Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: [PlayerFeature<MediaPlaybackState>, PlayerFeature<MediaPlaybackRateState>, PlayerFeature<MediaQualityState>, PlayerFeature<MediaAudioTrackState>, PlayerFeature<MediaVolumeState>, PlayerFeature<MediaTimeState>, PlayerFeature<MediaSourceState>, PlayerFeature<MediaBufferState>, PlayerFeature<MediaFullscreenState>, PlayerFeature<MediaPictureInPictureState>, PlayerFeature<MediaRemotePlaybackState>, PlayerFeature<MediaControlsState>, PlayerFeature<MediaTextTrackState>, PlayerFeature<MediaErrorState>, typeof metadataFeature] }` | — | Player configuration with features. |

#### Return Value

| Property | Type | Description |
| --- | --- | --- |
| `PlayerElement` | `typeof UIElement & (new (...args: any[]) => PlayerElement<VideoPlayerStore>)` | Configured player element class that owns the store and attachment lifecycle. |
| `PlayerController` | `{ new (host: ReactiveControllerHost & HTMLElement): PlayerController<VideoPlayerStore>; new <Result>(host: ReactiveControllerHost & HTMLElement, selector: Selector<VideoPlayerStore['state'], Result>): PlayerController<VideoPlayerStore, Result> }` | Player controller bound to this player's context. |
| `playerContext` | `Context<symbol, VideoPlayerStore>` | Context that carries the player store to descendant elements. |

### Audio

`createPlayer(config): CreatePlayerResult<AudioPlayerStore>`

Creates a typed HTML audio player class and bound controller.

#### Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: [PlayerFeature<MediaPlaybackState>, PlayerFeature<MediaPlaybackRateState>, PlayerFeature<MediaVolumeState>, PlayerFeature<MediaTimeState>, PlayerFeature<MediaSourceState>, PlayerFeature<MediaBufferState>, PlayerFeature<MediaErrorState>, typeof metadataFeature] }` | — | Player configuration with features. |

#### Return Value

| Property | Type | Description |
| --- | --- | --- |
| `PlayerElement` | `typeof UIElement & (new (...args: any[]) => PlayerElement<AudioPlayerStore>)` | Configured player element class that owns the store and attachment lifecycle. |
| `PlayerController` | `{ new (host: ReactiveControllerHost & HTMLElement): PlayerController<AudioPlayerStore>; new <Result>(host: ReactiveControllerHost & HTMLElement, selector: Selector<AudioPlayerStore['state'], Result>): PlayerController<AudioPlayerStore, Result> }` | Player controller bound to this player's context. |
| `playerContext` | `Context<symbol, AudioPlayerStore>` | Context that carries the player store to descendant elements. |

### Generic

`createPlayer<const Features extends AnyPlayerFeature[]>(config): CreatePlayerResult<PlayerStore<Features>>`

Creates a typed HTML player class with custom features.

#### Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: Features }` | — | Player configuration with features. |

#### Return Value

| Property | Type | Description |
| --- | --- | --- |
| `PlayerElement` | `typeof UIElement & (new (...args: any[]) => PlayerElement<PlayerStore<Features>>)` | Configured player element class that owns the store and attachment lifecycle. |
| `PlayerController` | `{ new (host: ReactiveControllerHost & HTMLElement): PlayerController<PlayerStore<Features>>; new <Result>(host: ReactiveControllerHost & HTMLElement, selector: Selector<PlayerStore<Features>['state'], Result>): PlayerController<PlayerStore<Features>, Result> }` | Player controller bound to this player's context. |
| `playerContext` | `Context<symbol, PlayerStore<Features>>` | Context that carries the player store to descendant elements. |

---

HTML documentation: https://videojs.org/docs/framework/html/llms.txt
All documentation: https://videojs.org/llms.txt
