# MixcloudAudio

Audio component that plays Mixcloud shows through the Mixcloud Widget

The [Mixcloud Widget](https://www.mixcloud.com/developers/widget/) plays Mixcloud shows. This component drives the widget and reports its state like a native `<audio>` element, so a [player skin](https://videojs.org/docs/framework/react/guides/skins) can control it.

## Import

```bash
pnpm add @videojs/mixcloud-audio
```

```tsx
import { MixcloudAudio } from '@videojs/react/media/mixcloud-audio';
```

## Load a source

`src` takes either of these Mixcloud URLs:

- A show: `mixcloud.com/<user>/<show>/`
- A widget embed URL with its widget parameters, as Mixcloud’s embed code gives it: `www.mixcloud.com/widget/iframe/?feed=...`

`www.` and `m.` links work too. Profiles, playlists, and other pages that list shows are not supported.

```tsx
<MixcloudAudio src="https://www.mixcloud.com/spartacus/lambiance/" />
```

## Controls

The widget is the only UI Mixcloud offers, so `controls` decides whether the component is visible at all. Without `controls`, the component hides itself and plays as an invisible transport: a hidden iframe still loads and plays, so a [player skin](https://videojs.org/docs/framework/react/guides/skins) drives playback while the widget takes no room on the page.

```tsx
<AudioPlayer>
  <AudioSkin>
    <MixcloudAudio src="https://www.mixcloud.com/spartacus/lambiance/" />
  </AudioSkin>
</AudioPlayer>
```

With `controls`, the component shows the widget, and the audio skin draws its controls below it, so the two do not overlap. Anything the listener does in the widget shows in the skin: play, pause, and seek. The widget is also the part of the page that credits the show and links back to it on Mixcloud (see [Mixcloud’s terms of use](#mixclouds-terms-of-use)).

```tsx
<MixcloudAudio src="https://www.mixcloud.com/spartacus/lambiance/" controls />
```

With `controls`, the component is 120 pixels tall by default, Mixcloud’s classic layout. Set a height to change it. With [widget parameters](#widget-parameters), `mini: true` selects the mini layout, which needs 60 pixels, and `hide_cover: false` selects the picture layout, a large cover that needs about 400 pixels. The widget hides the Mixcloud logo when it is narrower than 560 pixels.

## Widget parameters

By default, the component sets `hide_cover`, so the widget does not use its picture layout. Change it, or set `mini`, `hide_artwork`, `hide_tracklist`, or `light`, under `source.engine.mixcloud`, spelled as Mixcloud spells them:

```tsx
<MixcloudAudio
  source={{
    src: 'https://www.mixcloud.com/spartacus/lambiance/',
    engine: { mixcloud: { light: true, hide_artwork: true } },
  }}
/>
```

Engine options win over the parameters of a widget embed URL, which win over the defaults. Set an option to `null` to drop a default. The widget applies `mini`, `hide_cover`, `hide_artwork`, `hide_tracklist`, and `light` without reloading the show. Other options reload it.

`light` is the only theme option: Mixcloud offers no other colors. The widget has no start time parameter, so it ignores `start_time`. Set `currentTime` instead.

## Behavior

- `play()` asks the widget to play. Browsers apply their autoplay policy to the widget as they do to any embed, so a play without a user gesture can be refused. Then the widget reports nothing, and the media stays paused.
- `autoplay` starts playback once the widget is ready, after the volume applies. The widget’s own `autoplay` parameter does the same and is never written to the widget URL.
- The widget loads from `player-widget.mixcloud.com`, the host that Mixcloud’s embed URL redirects to. A frame that redirects to another host loses the `autoplay` permission of the page, and then the widget cannot start playback from the skin.
- Seeking uses whole seconds, as the widget does. The widget ignores a seek before the show first plays, so the component keeps that seek and applies it when playback starts.
- Mixcloud limits seeks and replays for listeners without a Mixcloud subscription: 6 fast-forwards and 6 rewinds per show in a rolling 60-minute period, and 4 replays of a show in a rolling 14-day period (see [Mixcloud’s help center](https://help.mixcloud.com/hc/en-us/articles/360004054059-Why-are-there-limits-for-rewinds-and-fast-forwards)). When the widget refuses a seek, `currentTime` goes back to the position of the widget, and `seeked` fires.
- `loop` plays the show again from the start when it ends.
- Volume and mute both work. The widget has no separate mute, so muting sets its volume to `0`, and unmuting restores the previous level.
- `buffered` is always empty, because the widget does not report how much of the show it has loaded.
- The widget does not support playback rate, picture-in-picture, fullscreen, or text tracks, so the component offers none of them, and a player skin hides those controls.
- `preload`, `playsinline`, and `poster` are accepted for parity with `<audio>` but have no effect.
- A URL that is not a Mixcloud show reports a `MEDIA_ERR_SRC_NOT_SUPPORTED` error, and so does a show the widget cannot find, such as a private or removed show. A show whose audio the widget does not load within 15 seconds reports a `MEDIA_ERR_NETWORK` error. An error the widget reports before playback starts is a `MEDIA_ERR_SRC_NOT_SUPPORTED` error, and an error during playback is a `MEDIA_ERR_NETWORK` error.

## Mixcloud’s terms of use

The Mixcloud widget is subject to Mixcloud’s own terms. Before you use this component, read the [Mixcloud Terms of Use](https://www.mixcloud.com/terms/) and the [Widget documentation](https://www.mixcloud.com/developers/widget/) to learn where and how you can use the widget. To keep the widget visible on the page, set `controls`.

## Privacy

The widget is subject to Mixcloud’s own privacy policy. Read the [Mixcloud Privacy Policy](https://www.mixcloud.com/privacy/) to learn what the widget collects from visitors and what you must do when you embed it. The widget loads from Mixcloud as soon as the component renders, so to wait for a visitor’s consent, render the component only after the visitor agrees.

## Examples

### Basic Usage

**App.tsx**

```tsx
import { MixcloudAudio } from '@videojs/react/media/mixcloud-audio';

export default function BasicUsage() {
  return (
    <div className="mixcloud-audio">
      <MixcloudAudio src="https://www.mixcloud.com/spartacus/lambiance/" controls />
    </div>
  );
}
```

**App.css**

```css
.mixcloud-audio {
  width: 100%;
}
```

## API Reference

### Props

Accepts these Video.js-specific props:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `autoplay` | `boolean` | `false` | Starts playback once the widget is ready, after the volume applies. Browsers may refuse it. |
| `controls` | `boolean` | `false` | |
| `defaultMuted` | `boolean` | `false` | |
| `loop` | `boolean` | `false` | Plays the show again from the start when it ends. |
| `muted` | `boolean` | `false` | |
| `playsInline` | `boolean` | `true` | |
| `poster` | `string` | `''` | |
| `preload` | `MediaPreloadType` | `'metadata'` | |
| `source` | `{ src?: string; engine?: MixcloudSourceEngineConfig } \| null` | `null` | Mixcloud show URL in `src`, widget parameters under `engine.mixcloud`. Replacing it re-derives `src`. |
| `src` | `string` | `''` | |

### Engine options

#### `source.engine.mixcloud`

Pass Mixcloud Widget options under `source.engine.mixcloud`, spelled exactly as Mixcloud spells them, plus anything Mixcloud adds next. [Media Sources](https://videojs.org/docs/framework/react/guides/media-sources) covers how engine options fit into a structured source.

The show comes from `src`, so `feed` has no `engine.mixcloud` spelling. `referrerPolicy` sets the `referrerpolicy` of the widget iframe rather than a widget parameter.

| Option | Type | Description |
| --- | --- | --- |
| `autoplay` | `boolean \| null \| undefined` | Start playback once the widget is ready, as `autoplay` does. Never written to the URL. |
| `hide_artwork` | `boolean \| null \| undefined` | Hide the show's artwork. |
| `hide_cover` | `boolean \| null \| undefined` | Hide the picture layout, a large cover that needs about 400 pixels of height. Defaults to `true`. |
| `hide_tracklist` | `boolean \| null \| undefined` | Hide the tracklist. |
| `light` | `boolean \| null \| undefined` | The light theme. Mixcloud offers no other colors. |
| `mini` | `boolean \| null \| undefined` | The mini layout, 60 pixels tall, without the repost, favorite, and share buttons. |
| `referrerPolicy` | `ReferrerPolicy \| undefined` | `referrerpolicy` for the widget iframe. Not a widget parameter. |

### Refs

#### `ref`

Forwards to the rendered `<iframe>`, an [HTMLIFrameElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement). The `<iframe>` cannot play or seek; use [`mediaRef`](#mediaref) for playback.

```tsx
const ref = useRef<HTMLIFrameElement>(null);

<MixcloudAudio ref={ref} />
```

#### `mediaRef`

Receives the `MixcloudAdapter` that drives the embed, which exposes media methods and properties such as `play()` and `currentTime`. It is set once the adapter has attached to the `<iframe>`, so its `target` is already that element, and it is cleared when the element unmounts.

```tsx
import type { MixcloudAdapter } from '@videojs/mixcloud-audio';

const mediaRef = useRef<MixcloudAdapter>(null);

<MixcloudAudio mediaRef={mediaRef} />

// In an event handler
mediaRef.current?.play();
```

### Events

Handle standard media events with React event props such as `onPlay` and `onTimeUpdate`. The adapter dispatches them, not the `<iframe>`. For events without a React prop, call `addEventListener` on the adapter from `mediaRef`.

---

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