# Live

Live edge state for the player store

Tracks when playback is at the live edge and what kind of live window the stream provides.

- `liveEdgeStart` is the playback time where the live edge begins. Playback counts as live when `currentTime` reaches this value.
- `targetLiveWindow` describes the kind of live window. Despite its name, it does not report a number of seconds: `0` means a sliding live window, `Infinity` means a live event with playback history, and `NaN` means on-demand or not known yet.

Both values are `NaN` when the media does not provide live-edge information.

Use the [buffer feature](https://videojs.org/docs/framework/react/reference/api/feature-buffer) to find the times a viewer can seek to. Its `seekable` value contains `[start, end]` pairs. On a sliding live stream, the oldest and newest available times both move forward.

The live presets do not include the [stream type feature](https://videojs.org/docs/framework/react/reference/api/feature-stream-type), so `selectStreamType` returns `undefined` unless you add it to a custom player.

The [time feature](https://videojs.org/docs/framework/react/reference/api/feature-time) reports the end of the available live video as `duration`, even when the browser reports `Infinity`. To check for live playback, use `!Number.isNaN(targetLiveWindow)` instead of checking `duration`.

## Import

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

The `liveAudioFeatures` and `liveVideoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.

## API Reference

### State

| Property | Type | Description |
| --- | --- | --- |
| `liveEdgeStart` | `number` | Playback time where the live edge begins. Playback is live when `currentTime >= liveEdgeStart`. `NaN` when the stream is not live or the value is unknown. |
| `targetLiveWindow` | `number` | Describes the kind of live window available. This value is not a duration. `0` for a sliding live window, `Infinity` for a live event with playback history, and `NaN` for on-demand or unknown. |

### Selector

Pass `selectLive` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to live state. Returns `undefined` if the live feature is not configured.

**LiveEdgeIndicator.tsx**

```tsx
import { selectLive, selectTime, usePlayer } from '@videojs/react';

function LiveEdgeIndicator() {
  const live = usePlayer(selectLive);
  const time = usePlayer(selectTime);
  if (!live || Number.isNaN(live.targetLiveWindow)) return null;

  const atEdge = time != null && time.currentTime >= live.liveEdgeStart;
  return <span className="live-indicator">{atEdge ? 'LIVE' : 'BEHIND LIVE'}</span>;
}
```

## Jump to the live edge

Use the packaged live button for the usual jump-to-live behavior. It combines the live, time, and buffer features, then seeks to the end of the last `seekable` range. It also manages its accessible label and disabled state.

**StationLiveButton.tsx**

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

export function StationLiveButton() {
  return <LiveButton className="station-live-button">On air</LiveButton>;
}
```

Put the button in your custom controls and style it there. Do not calculate the destination from `duration` or set `currentTime` to `Infinity`.

If you need different behavior, you can combine the same public features yourself:

- Read `targetLiveWindow` from `selectLive` to check that the source is live.
- Read the newest available time from the last range returned by `selectBuffer`.
- Call the `seek()` action returned by `selectTime` with that time.

**CustomLiveButton.tsx**

```tsx
import { selectBuffer, selectLive, selectTime, usePlayer } from '@videojs/react';

export function CustomLiveButton() {
  const live = usePlayer(selectLive);
  const time = usePlayer(selectTime);
  const buffer = usePlayer(selectBuffer);
  const newestTime = buffer?.seekable.at(-1)?.[1];

  const jumpToLive = () => {
    if (!time || newestTime === undefined || !Number.isFinite(newestTime)) return;
    void time.seek(newestTime);
  };

  const disabled = !live || Number.isNaN(live.targetLiveWindow) || !Number.isFinite(newestTime);

  return (
    <button type="button" disabled={disabled} onClick={jumpToLive}>
      Go live
    </button>
  );
}
```

---

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