# Dialog

A modal dialog for application content, including players opened from a thumbnail

## Import

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

## Anatomy

```tsx
<Dialog.Root>
  <Dialog.Trigger />
  <Dialog.Backdrop />
  <Dialog.Popup>
    <Dialog.Title />
    <Dialog.Description />
    <Dialog.Close />
  </Dialog.Popup>
</Dialog.Root>
```

## Behavior

`Dialog` provides the modal interaction and accessibility behavior while your application supplies the content and styling. It is the general-purpose replacement for a `ModalDialog` use case such as opening a player from a thumbnail.

Use `Dialog.Trigger` to open the dialog. Use `open` with `onOpenChange` for controlled state, or `defaultOpen` for uncontrolled state. `Root` provides state without rendering an element, while `Popup` renders only while the dialog is open or closing.

The close part and Escape close the dialog. Other buttons inside the dialog, including player controls, do not close it.

Your application decides what opening and closing mean for the media. The example starts playback after opening and pauses it as soon as the dialog closes. Handle the promise returned by `play()`, since browsers can reject autoplay when it is not allowed.

Use [`AlertDialog`](https://videojs.org/docs/framework/react/reference/components/alert-dialog) for urgent messages that require acknowledgement. [`ErrorDialog`](https://videojs.org/docs/framework/react/reference/components/error-dialog) is the player-error specialization. Both use the same dialog behavior.

## Styling

Use the open and transition data attributes to style the modal and its animations:

| Attribute | Description |
| --- | --- |
| `data-open` | Present while the dialog is open, including its closing transition |
| `data-starting-style` | Present at the start of the opening transition |
| `data-ending-style` | Present during the closing transition |

React renders the backdrop and popup as separate DOM elements. Add a `className` to each part and style their transitions independently:

```css
.dialog-backdrop[data-starting-style],
.dialog-backdrop[data-ending-style],
.dialog-popup[data-starting-style],
.dialog-popup[data-ending-style] {
  opacity: 0;
}
```

## Accessibility

The popup uses `role="dialog"` and `aria-modal="true"`. Include `Dialog.Title` so the dialog always has an accessible name. `Dialog.Description` is optional. These parts receive generated IDs connected through `aria-labelledby` and `aria-describedby`. The trigger receives `aria-haspopup`, `aria-controls`, and `aria-expanded`.

When the dialog opens, focus moves to an element with `autofocus`, the first focusable element, or the popup itself. Tab and Shift + Tab keep focus within the dialog, and content outside the dialog becomes inert. Closing restores focus to the trigger, or to the element that was focused before the dialog opened.

Dialogs inside the same player container share modal coordination. Opening one closes the current dialog and dismisses any open menu, tooltip, or popover before focus moves into the new popup.

## Examples

### Player modal

Open the video from its thumbnail. Playback starts when the dialog opens and pauses when it closes.

**App.tsx**

```tsx
import { Container, createPlayer, Dialog } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import { useEffect, useRef, useState } from 'react';

import './BasicUsage.css';

const { Player } = createPlayer({ features: videoFeatures });

export default function BasicUsage() {
  const [open, setOpen] = useState(false);
  const videoRef = useRef<HTMLVideoElement>(null);

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    if (open) void video.play().catch(() => {});
    else video.pause();
  }, [open]);

  return (
    <div className="react-dialog-basic">
      <Dialog.Root open={open} onOpenChange={setOpen}>
        <Dialog.Trigger className="react-dialog-basic__trigger">
          <img src="https://image.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/thumbnail.jpg" alt="" />
          <span>Play the video</span>
        </Dialog.Trigger>
        <Dialog.Backdrop className="react-dialog-basic__backdrop" />
        <Dialog.Popup className="react-dialog-basic__dialog">
          <div className="react-dialog-basic__popup">
            <Dialog.Title className="react-dialog-basic__title">Video title</Dialog.Title>
            <Dialog.Description className="react-dialog-basic__description">
              A video opened from a thumbnail.
            </Dialog.Description>
            <Dialog.Close className="react-dialog-basic__close">Close</Dialog.Close>
            <Player>
              <Container>
                <Video ref={videoRef} src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" controls muted playsInline />
              </Container>
            </Player>
          </div>
        </Dialog.Popup>
      </Dialog.Root>
    </div>
  );
}
```

**App.css**

```css
.react-dialog-basic {
  position: relative;
  min-height: 320px;
}

.react-dialog-basic__trigger {
  display: grid;
  gap: 10px;
  width: min(100%, 420px);
  padding: 0 0 12px;
  overflow: hidden;
  color: white;
  cursor: pointer;
  background: #111827;
  border: 0;
  border-radius: 12px;
}

.react-dialog-basic__trigger img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.react-dialog-basic__backdrop {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgb(3 7 18 / 80%);
  transition: opacity 200ms ease;
}

.react-dialog-basic__dialog {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  transition: opacity 150ms ease;
}

.react-dialog-basic__backdrop[data-starting-style],
.react-dialog-basic__backdrop[data-ending-style],
.react-dialog-basic__dialog[data-starting-style],
.react-dialog-basic__dialog[data-ending-style] {
  opacity: 0;
}

.react-dialog-basic__popup {
  position: relative;
  width: 100%;
  max-width: 560px;
  padding: 16px;
  color: white;
  background: #111827;
  border-radius: 12px;
}

.react-dialog-basic__title {
  margin: 0 0 4px;
  font-size: 1.125rem;
  font-weight: 600;
}

.react-dialog-basic__description {
  margin: 0 0 12px;
}

.react-dialog-basic__close {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 6px 12px;
  color: #111827;
  cursor: pointer;
  background: white;
  border: 0;
  border-radius: 9999px;
}

.react-dialog-basic video {
  display: block;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .react-dialog-basic__backdrop,
  .react-dialog-basic__dialog {
    transition: none;
  }
}
```

## API Reference

### Root

Manages dialog state and provides it to the compound parts.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `closeOnEscape` | `boolean` | `true` | Whether pressing Escape closes the dialog. |
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled usage. |
| `open` | `boolean` | `false` | Controlled open state. When set, the consumer is responsible for toggling. |

#### State

State is accessible via the `render`, `className`, and `style` props.

| Property | Type | Description |
| --- | --- | --- |
| `transitionStarting` | `boolean` | Whether the open transition is in progress. |
| `transitionEnding` | `boolean` | Whether the close transition is in progress. |
| `open` | `boolean` | Whether the dialog is currently open. |
| `status` | `'idle' \| 'starting' \| 'ending'` | Current phase of the transition lifecycle. |
| `titleId` | `string \| undefined` | Element ID of the dialog title, used for `aria-labelledby`. |
| `descriptionId` | `string \| undefined` | Element ID of the dialog description, used for `aria-describedby`. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-open` | Present when the dialog is open. |
| `data-starting-style` | Present during the open transition. |
| `data-ending-style` | Present during the close transition. |

### Backdrop

Presentational layer behind a dialog while it is rendered, including its exit transition.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-open` | Present when the dialog is open. |
| `data-starting-style` | Present during the open transition. |
| `data-ending-style` | Present during the close transition. |

### Close

Renders a button that closes the dialog.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-open` | Present when the dialog is open. |
| `data-starting-style` | Present during the open transition. |
| `data-ending-style` | Present during the close transition. |

### Description

Renders the description announced with the dialog.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

### Popup

Renders the modal dialog while it is open.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-open` | Present when the dialog is open. |
| `data-starting-style` | Present during the open transition. |
| `data-ending-style` | Present during the close transition. |

### Title

Renders the heading that labels the dialog.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

### Trigger

Renders a button that opens the dialog.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: DialogState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: DialogState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: DialogState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-open` | Present when the dialog is open. |
| `data-starting-style` | Present during the open transition. |
| `data-ending-style` | Present during the close transition. |

---

React documentation: https://videojs.org/docs/framework/react/llms.txt
All documentation: https://videojs.org/llms.txt
