# Examples

> Players built with bobine, from a single prop to fully composed controls. Try prop combinations in the playground, or copy a recipe that already fits.

## Playground

Turn props on and off. The player and its code update together. Open [the examples page](https://bobine.fyi/docs/examples) to use it.

## Video recipes

Common jobs for VideoPlayer, each one a few props on the native element.

### Course lesson

Captions on from the start, and a persistKey shared by every lesson, so a viewer's speed and volume carry over to the next one.

```tsx
<VideoPlayer
  src="/lessons/01.mp4"
  tracks={[
    {
      src: "/lessons/01.en.vtt",
      srcLang: "en",
      label: "English",
      default: true,
    },
  ]}
  persistKey="course"
/>
```

### Ambient loop

A quiet clip for a hero or a product shot. Browsers only autoplay muted video, and playsInline keeps iOS from opening it fullscreen.

```tsx
<VideoPlayer
  src="/hero.mp4"
  autoPlay
  muted
  loop
  playsInline
  tooltips={false}
/>
```

### Square crop

frameClassName sizes the frame and className styles the video, so object-cover fills a square for a feed.

```tsx
<VideoPlayer
  src="/clip.mp4"
  frameClassName="aspect-square max-w-sm"
  className="object-cover"
/>
```

### Minimal controls

Compose the parts yourself when a preset shows too much. This one keeps a play button while paused and a seek bar, nothing else.

```tsx
<MediaRoot className="aspect-video overflow-hidden rounded-xl bg-media-scrim text-media-foreground">
  <MediaVideo src="/clip.mp4" className="size-full object-cover" />
  <MediaSurface />
  <MediaPlayButton className="absolute inset-0 m-auto size-12 rounded-full bg-media-scrim/50 backdrop-blur-md group-data-playing/media:pointer-events-none group-data-playing/media:opacity-0" />
  <MediaSeek className="absolute inset-x-4 bottom-4" />
</MediaRoot>
```

## Audio recipes

AudioPlayer adapts to its container, from a full card to a small pill.

### Podcast episode

A title, the show's name and a speed toggle. persistKey keeps the chosen speed for the next episode.

```tsx
<AudioPlayer
  src="/episodes/12.mp3"
  title="Episode 12"
  description="Field notes"
  showSpeed
  persistKey="podcast"
/>
```

### Voice note

No title and a rounded frame turn the card into a pill, the shape chat apps use for voice messages.

```tsx
<AudioPlayer
  src="/notes/voice.m4a"
  frameClassName="max-w-xs rounded-full"
/>
```

### Track list

Native events pass straight through. onPlay pauses the other tracks, so only one plays at a time.

```tsx
type AudioEvent = React.SyntheticEvent<HTMLAudioElement>

function pauseOthers(event: AudioEvent) {
  for (const audio of document.querySelectorAll("audio")) {
    if (audio !== event.currentTarget) audio.pause()
  }
}

{tracks.map((track) => (
  <AudioPlayer
    key={track.src}
    src={track.src}
    title={track.title}
    description={track.artist}
    onPlay={pauseOthers}
    frameClassName="rounded-none border-0 border-b last:border-b-0"
  />
))}
```
