# Add a poster and loading placeholder

Set the image shown before playback, then add a lightweight placeholder while it loads

A poster is the image shown before your video plays. There are several places to set one, so work down this list and stop at the first step that fits:

1. **Let the media provide it.** Some media components create a poster from the source, so there is nothing extra to host or keep in sync.
2. **Set `poster` on the player.** With a packaged skin, that is all a static image needs.
3. **Take control of the image** when you need a framework image component, a `<picture>` element, or a loading placeholder. The skin renders your image instead of its own.
4. **Keep it working after [adding the skin source to your project](https://videojs.org/docs/framework/react/guides/customize-skins#style-skin-source).** The player’s `poster` still reaches the Poster component your skin renders, or you can set the image’s source yourself.

## Let the media provide a poster

Some media components choose a poster for you. For example, `MuxVideo` creates one from its playback ID:

Install the React façade and the Mux playback adapter:

```bash
pnpm add @videojs/react @videojs/mux-video
```

```tsx
import '@videojs/react/video/skin.css';
import { MuxVideo } from '@videojs/react/media/mux-video';
import { VideoPlayer, VideoSkin } from '@videojs/react/video';

export function MyPlayer() {
  return (
    <VideoPlayer>
      <VideoSkin>
        <MuxVideo
          source={{
            playbackId: 'BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM',
            poster: { time: 2 },
          }}
          playsInline
        />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

## Set a poster on the player

When the media has no poster of its own, set the player’s `poster` to an image URL:

```tsx
import '@videojs/react/video/skin.css';
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';

export function MyPlayer() {
  return (
    <VideoPlayer poster="/poster.jpg">
      <VideoSkin>
        <Video src="/video.mp4" playsInline />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

If you use a ready-made skin, it includes [Poster](https://videojs.org/docs/framework/react/reference/components/poster) and shows the image automatically. If you build your own UI, add Poster yourself. It hides after the user plays or seeks.

When the player and the media component both choose a poster, the player’s `poster` wins. See [Metadata](https://videojs.org/docs/framework/react/reference/api/feature-metadata) for how the player combines those values.

Posters are decorative by default, with an empty `alt`. If the image communicates something that is not available elsewhere, add `alt` text when you customize the poster image.

## Take control of the poster image

The packaged skins also accept an image of your own, for when a plain `<img>` is not enough:

Pass `renderPoster` to the skin. Video.js passes the poster URL as `src` alongside the rest of the image props; `src` stays `undefined` until a poster URL resolves. That fits a framework image component:

```tsx
import '@videojs/react/video/skin.css';
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';
import NextImage from 'next/image';

export function MyPlayer() {
  return (
    <VideoPlayer poster="/poster.jpg">
      <VideoSkin
        renderPoster={({ src, ...props }) =>
          src ? <NextImage {...props} src={src} alt="" fill /> : null
        }
      >
        <Video src="/video.mp4" playsInline />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

## Add a loading placeholder

A placeholder is a tiny preview that appears while the poster downloads, so a slow poster does not leave the player as empty space. Keep it small enough to include with the page, such as a short data URL. Add it to the customized image from the previous section.

### Use your framework’s image component

If your image component supports placeholders, pass yours through `renderPoster`:

```tsx
import '@videojs/react/video/skin.css';
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';
import NextImage from 'next/image';

export function MyPlayer() {
  return (
    <VideoPlayer poster="/poster.jpg">
      <VideoSkin
        renderPoster={({ src, ...props }) =>
          src ? (
            <NextImage
              {...props}
              src={src}
              alt=""
              fill
              placeholder="blur"
              blurDataURL="data:image/webp;base64,…"
            />
          ) : null
        }
      >
        <Video src="/video.mp4" playsInline />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

Next.js generates blur data for static imports. Here, the poster URL arrives while the player runs, so provide `blurDataURL` yourself.

### Use a plain image

For more control, add the placeholder as a background on the image that `VideoSkin` draws:

```tsx
import '@videojs/react/video/skin.css';
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';

export function MyPlayer() {
  return (
    <VideoPlayer poster="/poster.jpg">
      <VideoSkin
        renderPoster={(props) => (
          <img
            {...props}
            style={{
              ...props.style,
              background:
                "url('data:image/webp;base64,…') var(--media-object-position, center) / contain no-repeat",
            }}
          />
        )}
      >
        <Video src="/video.mp4" playsInline />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

The examples use `contain` for both images. Use `cover` for both when the poster should fill the player. Keeping the same size and position prevents the image from jumping when the poster appears.

## Set the poster after adding skin source

Adding the skin source to your project changes nothing about the first two approaches: the player’s `poster`, or the poster the media provides, still reaches the [Poster](https://videojs.org/docs/framework/react/reference/components/poster) component your skin renders.

`Poster.Root` owns visibility and loading state. `Poster.Image` renders the image and fills its `src` from the player, so `srcSet`, `sizes`, `loading`, and the rest of the image attributes are yours. Provide your own `src` or `srcSet` and Video.js leaves the image alone.

When your UI already includes Poster, apply a placeholder through the image’s `render` prop:

```tsx
<Poster.Root>
  <Poster.Image
    render={(props) => (
      <img
        {...props}
        style={{
          ...props.style,
          background:
            "url('data:image/webp;base64,…') var(--media-object-position, center) / contain no-repeat",
        }}
      />
    )}
  />
</Poster.Root>
```

## Troubleshooting

### No poster appears

Check that the player has a `poster` or that the media component can create one from its source.

### The final poster never appears

Pass the `src` from `renderPoster` to your image. If your image component requires a source, return `null` until `src` is available.

### The placeholder and poster use different crops

Use the same fit for both images: pair `background-size: contain` with `object-fit: contain`, or use `cover` for both.

### The placeholder adds too much page weight

Use fewer pixels and stronger compression. The placeholder only needs to suggest the poster’s color and shape until the full image appears.

## Related pages

### Components

- [Poster](https://videojs.org/docs/framework/react/reference/components/poster): Poster image component that displays a thumbnail until video playback starts

### API

- [Metadata](https://videojs.org/docs/framework/react/reference/api/feature-metadata): Resolved title and poster values for the player store

### Guides

- [Customize skins](https://videojs.org/docs/framework/react/guides/customize-skins#style-a-packaged-skin): Style a packaged Video.js skin or add its source to change controls, layout, styles, and interactions
- [Build your own UI component](https://videojs.org/docs/framework/react/guides/build-your-own-component): Create custom player controls that read state, dispatch actions, and stay accessible.

---

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