---
title: Write reference pages
description: How to create API reference pages for the Video.js documentation site
---

import Aside from '@/components/Aside.astro';
import DocsLink from '@/components/docs/DocsLink.astro';

This guide covers how to create API reference pages — both component references and util references (hooks, controllers, mixins) under `reference/` in the docs sidebar.

<Aside type="tip">
Reference pages use the portable `write-api-reference` agent skill. Invoke `/write-api-reference play-button` in clients that expose skills as slash commands.
</Aside>

## Prerequisites

Before creating a reference page, the component must exist in:

- **Core**: `packages/core/src/core/ui/{name}/{name}-core.ts` (props, state, behavior)
- **HTML**: `packages/html/src/ui/{name}/` (custom element)
- **React**: `packages/react/src/ui/{name}/` (React component)

The component should be feature-complete enough that its props, state, and data attributes are stable.

## Generate the API reference JSON

The api-docs-builder extracts type information from TypeScript sources and outputs JSON files that `<ComponentReference />` and `<UtilReference />` components render as tables.

```bash
pnpm -F site api-docs
```

This generates JSON to `site/src/content/generated-component-reference/{name}.json` and `site/src/content/generated-util-reference/{name}.json`. These files are gitignored and regenerated automatically on `pnpm dev` and `pnpm build`.

### Builder naming conventions

The builder relies on file naming conventions to discover components:

| Convention | Pattern | Example |
|------------|---------|---------|
| Core file | `{name}-core.ts` | `play-button-core.ts` |
| Data attrs | `{name}-data-attrs.ts` | `play-button-data-attrs.ts` |
| HTML element | `{name}-element.ts` | `play-button-element.ts` |
| React component | `packages/react/src/ui/{name}/` | `packages/react/src/ui/play-button/` |
| Multi-part detection | `index.parts.ts` | `packages/react/src/ui/slider/index.parts.ts` |

If the builder output is missing or incomplete, check that your files match these conventions. See `.agents/skills/write-api-reference/references/builder-conventions.md` for the full list.

## Create demo files

Each reference page needs at least a BasicUsage demo in both HTML and React.

### HTML demo (4 files)

```
src/components/docs/demos/{name}/html/css/
├── BasicUsage.astro    # Wrapper: imports CSS, renders HTML, bundles script
├── BasicUsage.html     # Markup only (no <style> or <script>)
├── BasicUsage.css      # Styles
└── BasicUsage.ts       # Side-effect imports for custom element registration
```

The `.astro` wrapper ties everything together:

```astro
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
import './BasicUsage.css';
---
<HtmlDemo html={html} />
<script>
  import './BasicUsage.ts';
</script>
```

### React demo (2 files)

```
src/components/docs/demos/{name}/react/css/
├── BasicUsage.tsx      # React component
└── BasicUsage.css      # Styles
```

### BEM naming

Use BEM class names for CSS scoping. The block name follows the pattern `{framework}-{component}-{variant}`:

```css
/* HTML demo */
.html-play-button-basic__button { /* ... */ }

/* React demo */
.react-play-button-basic__button { /* ... */ }
```

React and HTML demos for the same variant should use matching BEM structures.

### Video and poster sources

Demo media sources live in `site/src/consts.ts`. HTML and React demo files use placeholders from `site/scripts/replace-demo-placeholders.ts`; the site resolves them for both the live demo and its source tab, so displayed source remains copy-paste ready.

Use the matching placeholder instead of hardcoding a media URL in a demo:

| Purpose | Demo placeholder |
| --- | --- |
| Default MP4 | `{{VJS10_DEMO_VIDEO_MP4}}` |
| Default HLS | `{{VJS10_DEMO_VIDEO_HLS}}` |
| Default poster | `{{VJS10_DEMO_POSTER}}` |
| Default storyboard | `{{VJS10_DEMO_STORYBOARD}}` |
| Background video | `{{VJS10_DEMO_BACKGROUND_VIDEO_MP4}}` |
| DASH | `{{VJS10_DEMO_DASH}}` |
| Quality renditions | `{{VJS8_DEMO_VIDEO_HLS}}` |
| Multi-language audio and renditions | `{{VJS10_MULTI_AUDIO_DEMO_VIDEO_HLS}}` |

The default demo assets are hosted by [Mux](https://www.mux.com?utm_source=videojs&utm_campaign=vjs10):

```
Video: https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4
Poster: https://image.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/thumbnail.jpg
```

Components that need adaptive streams use `<HlsJsVideo>` / `<hlsjs-video>` with an HLS source instead. Pick by what the component consumes:

```
Renditions (quality):          https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4.m3u8
Multi-language audio (+ renditions): https://stream.mux.com/s41JYeqIpBMBzE4OzxDyGR2yrp2hD1CQ6gJN9SlVGDQ.m3u8
```

The default mp4 has a single rendition and a single audio track — quality and audio-track demos render nothing against it.

All demo videos use `autoplay muted playsinline loop` (React: `autoPlay muted playsInline loop`).

## Create the MDX reference page

Create `site/src/content/docs/reference/{name}.mdx` with this structure:

### Frontmatter

```yaml
---
title: PlayButton
frameworkTitle:
  html: media-play-button
description: A button component for playing and pausing media playback
---
```

Use `frameworkTitle` to show the HTML custom element tag name when the HTML framework is selected.

### Imports

```tsx
import ComponentReference from "@/components/docs/api-reference/ComponentReference.astro";
import FrameworkCase from "@/components/docs/FrameworkCase.astro";
import Demo from "@/components/docs/demos/Demo.astro";

{/* React demos */}
import BasicUsageDemoReact from "@/components/docs/demos/{name}/react/css/BasicUsage";
import basicUsageReactTsx from "@/components/docs/demos/{name}/react/css/BasicUsage.tsx?raw";
import basicUsageReactCss from "@/components/docs/demos/{name}/react/css/BasicUsage.css?raw";

{/* HTML demos */}
import BasicUsageDemoHtml from "@/components/docs/demos/{name}/html/css/BasicUsage.astro";
import basicUsageHtml from "@/components/docs/demos/{name}/html/css/BasicUsage.html?raw";
import basicUsageHtmlCss from "@/components/docs/demos/{name}/html/css/BasicUsage.css?raw";
import basicUsageHtmlTs from "@/components/docs/demos/{name}/html/css/BasicUsage.ts?raw";
```

#### Import naming conventions

| What | Naming pattern | Example |
|------|---------------|---------|
| React demo component | `{Variant}DemoReact` | `BasicUsageDemoReact` |
| React source (TSX) | `{variant}ReactTsx` | `basicUsageReactTsx` |
| React source (CSS) | `{variant}ReactCss` | `basicUsageReactCss` |
| HTML demo component | `{Variant}DemoHtml` | `BasicUsageDemoHtml` |
| HTML source (HTML) | `{variant}Html` | `basicUsageHtml` |
| HTML source (CSS) | `{variant}HtmlCss` | `basicUsageHtmlCss` |
| HTML source (TS) | `{variant}HtmlTs` | `basicUsageHtmlTs` |

### Page sections

After imports, the page follows this order:

1. **Anatomy** — show part nesting with self-closing placeholders for each framework using `<FrameworkCase>`, following the [Base UI anatomy convention](https://base-ui.com/react/overview/quick-start). No hooks, state, handlers, or option mapping — working code belongs in Examples.
2. **Prose sections** (optional, as needed):
   - **Behavior** — state transitions, timing, interaction logic
   - **Styling** — data attribute CSS selectors
   - **Accessibility** — ARIA attributes, keyboard interactions
   - Other sections as appropriate
3. **Examples** — at least BasicUsage, wrapped in `<Demo>` with `<FrameworkCase>`
4. **`<ComponentReference />`** — renders the generated JSON as props, state, and data attribute tables

```mdx
<ComponentReference component="PlayButton" />
```

The component automatically handles single-part and multi-part layouts.

## Create a util reference page

Util reference pages document React hooks/utilities and HTML controllers/mixins. Unlike component pages, they don't have demos or anatomy sections.

### Structure

```mdx
---
title: usePlayer
description: Hook to access the player store
---

import UtilReference from "@/components/docs/api-reference/UtilReference.astro";

## Import

\`\`\`tsx
import { usePlayer } from '@videojs/react';
\`\`\`

## Usage

Explain usage patterns, overloads, and code examples.

<UtilReference util="usePlayer" />
```

### Key differences from component pages

- No `frameworkTitle` — util pages are framework-specific (React-only or HTML-only)
- No demos or anatomy — focus on import, usage examples, and the generated reference tables
- Use `<UtilReference util="..." />` instead of `<ComponentReference component="..." />`
- The `util` prop takes the PascalCase or camelCase name (e.g., `"usePlayer"`, `"PlayerController"`)

### Util auto-discovery rules

The builder discovers utils from package entry points two ways:

**Naming conventions** (no tag needed):
- `use*` hooks: `usePlayer`, `useMedia`
- `*Controller` classes: `PlayerController`, `StoreController`
- `create*` factories and mixins: `createPlayer`, `createProviderMixin`
- `select*` selectors: `selectPlayback`, `selectVolume`

**`@public` JSDoc tag** (everything else):
- Utilities that don't match a convention: `mergeProps`, `renderElement`
- Context objects: `playerContext`

If your export matches a naming convention, skip the `@public` tag.

### The slug prop

The `util` prop takes the export name (`"usePlayer"`, `"PlayerController"`). If the generated JSON slug doesn't match `kebabCase(util)` -- like the HTML `createPlayer` whose slug is `html-create-player` -- pass `slug` explicitly:

```mdx
<UtilReference util="createPlayer" slug="html-create-player" />
```

### Generated JSON

Util reference JSON is at `site/src/content/generated-util-reference/{slug}.json`. The builder generates it from the discovery pipeline in `site/scripts/api-docs-builder/src/util-handler.ts`.

## Add to the sidebar

Open `site/src/docs.config.ts` and add your page to the appropriate category under **API Reference**:

- **UI Components** — UI component reference pages, sorted alphabetically
- **Media Components** — media elements that handle streaming protocols and playback
- **Player Features** — feature modules passed to `createPlayer`
- **Utilities** — hooks, utilities, controllers, and mixins. Commonly used entries
  go at the top of the list; everything else follows.

The categories are flat by design, so don't nest a new subsection inside them. Mark framework-specific entries with `frameworks: ['react']` or `frameworks: ['html']` on the entry itself.

```ts
{
  sidebarLabel: 'UI Components',
  contents: [
    // sorted alphabetically
    { slug: 'reference/play-button' },
    { slug: 'reference/your-component' }, // add here
  ],
},
```

## Verify

1. Run `pnpm dev` from the repo root
2. Navigate to your reference page in both HTML and React framework modes
3. Confirm the anatomy, demos, and API reference tables render correctly
4. Check that the page appears in the sidebar

## Examples

For reference, look at existing pages:

**Component pages:**
- <DocsLink slug="reference/play-button">PlayButton</DocsLink> — single-part, interactive
- <DocsLink slug="reference/controls">Controls</DocsLink> — behavior-heavy (auto-hide)
- <DocsLink slug="reference/time">Time</DocsLink> — multi-part, formatting

**Util pages:**
- <DocsLink slug="reference/use-player">usePlayer</DocsLink> — React hook, multi-overload
- <DocsLink slug="reference/player-controller">PlayerController</DocsLink> — HTML controller
