# Replay bar

A React component in [Beamline](https://beamline.io/)'s [Data & tables](https://beamline.io/components/data) category. Live demo: https://beamline.io/components/replay-bar

Rewind for anything live: pause a stream, scrub back to the moment you missed (markers for the ones that matter), step one update at a time, replay at a speed and return to live in one press, counting what you have not seen; the track is the arrivals themselves and the live part is unchanged.

## Use it for

- Any live part whose recent past matters: a deploy or agent log, a run trace, a queue or board, a live chart.
- One screen of live parts that should rewind together to the same moment (one recorder for the whole screen's value).

## Not for

- Seeking in audio or video ([scrub-bar](https://beamline.io/components/scrub-bar)).
- Choosing a value on a scale ([scrubber](https://beamline.io/components/scrubber)).

## Anatomy

- row: play/pause, the mode in words (a still dot for Live; Paused, Held, Replaying 2×), speed, and Back to live with the count of unseen updates and its L key
- track, on its own row: a Scrubber over the recording whose ticks are the arrivals (each arrival weighs on its neighbours, so a burst stands taller than a lone line), its readout the moment shown ("11s before live"; live, how many updates), markers as labelled ticks on the update they name

## States

- waiting (nothing recorded yet)
- live (the knob rides the newest arrival; the readout counts updates)
- ended (with `ended`, at the newest value the bar says Ended, without the live dot; rewound, the readout says how far before the end and the button offers To the end)
- held (the pointer or focus on the live part pauses it; it runs on when they leave)
- paused (the part holds still; Back to live counts updates never shown)
- scrubbing (the part shows the recorded moment; Escape puts back what was showing, live included)
- replaying at 1×, 2× or 4× (the knob glides from update to update; it returns to live when it catches up)

## Keyboard

- Tab moves through the bar: play/pause, speed, back to live, the moment slider
- On the slider: ←/→ step one update, Page Up/Down jump between markers (past the last, to the end), Home/End go to the first and the last update on the track (paused, the track holds still; L or Back to live reaches the newest), Space plays or pauses
- L anywhere in the bar goes back to live

## Motion

Nothing moves at rest beyond what arrives. While replaying the bar renders once per shown update and the knob glides between updates over the fast duration; the Scrubber's lens and hand physics apply to the track. Reduced motion jumps the knob instead of gliding it.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `recorder` (required) | `Recorder (from useReplay)` | — | The recorder from `const { shown, state, recorder } = useReplay(value)`; `import { ReplayBar, useReplay, holdWhileReading } from "@beamline/ui/replay-bar"`. Each new value (a new array or object, or a new number) is one update; give the live part `shown`, and its own live badge `state.mode === "live" && !state.held` (modes: "live", "paused", "replay"; `held` while someone reads it, which also stops a replay under the reader). A list can keep its later lines in view, dim until the replay reaches them: `const { shown, frozen, recorder } = useReplay(lines)` then `<LogViewer lines={lines} playhead={frozen ? shown.length : null} />` (`frozen`: a recorded value is showing). `useReplay(value, { capacity = 2000, enabled = true })`; a number works as well as a list. Spread `holdWhileReading(recorder)` ({ onPointerEnter, onPointerLeave, onFocus, onBlur }) on the part to pause while it is read. Required. |
| `markers` | `{ t: number; label: string; priority?: number }[]` | — | Notable moments (`ReplayMarker` { t, label, priority }, exported beside the bar: `import { ReplayBar, type ReplayMarker } from "@beamline/ui/replay-bar"`; a new array each render is fine) on the track (a higher priority keeps its words when they crowd): `t` in ms from `Date.now()`, the recorder's own clock: stamp it when the value it names arrives; each sits on the first update at or after `t`, so a jump shows the event it names. Page Up/Down jump between them. |
| `format` | `(at: number, live: number) => string` | — | Text for a moment: `at` is the shown time, `live` the newest arrival (ms on the recorder's clock). Defaults to "12s before live" ("12s before the end" once `ended`). |
| `describe` | `(value: T, index: number, count: number) => string` | — | What a recorded value means, for screen readers stepping through it: it receives the value as you passed it to `useReplay` at that update (for a list, the whole list then), its index and the count ("error: i-0c2e failed"). |
| `speeds` | `number[]` | `[1, 2, 4]` | Replay speeds the speed control steps through. Default [1, 2, 4]. |
| `label` | `string` | `"Replay"` | Accessible name of the bar. Defaults to "Replay". |
| `className` | `string` | — | Classes on the documented visual root. |
| `classNames` | `Partial<Record<"root" \| "track" \| "live" \| "speed" \| "mode" \| "play", string>>` | — | Classes for the named visual parts. |
| `style` | `CSSProperties` | — | Inline styles on the documented visual root. |
| `ref` | `Ref<HTMLDivElement>` | — | Receives the root element. |
| `ended` | `boolean` | — | The source has finished (a deploy that completed, a stream that closed): at the newest value the bar says Ended instead of Live. |
| `recorded` | `((count: number) => string)` | — | The readout while live, in your word for what a recorded value is ("18 lines recorded"). `count` is the number of recorded updates (one per new value), the number of lines only when lines arrive one at a time. Defaults to "N updates recorded". |

## Built from

- [Button](https://beamline.io/components/button.md)
- [Kbd](https://beamline.io/components/kbd.md)
- [Scrubber](https://beamline.io/components/scrubber.md)
- [Spinner](https://beamline.io/components/spinner.md)

## Import

```tsx
import { ReplayBar } from "@beamline/replay-bar";
```

## Get it

Replay bar is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in Data & tables

- [Living table](https://beamline.io/components/living-table): A table that shows as much as fits on both axes: width decides which columns show (the rest ride in the name line, moving there rather than vanishing)…
- [Availability layers](https://beamline.io/components/availability-layers): A week of bookable start times whose closed times explain themselves: every availability rule (hours, time off, bookings, buffers, rooms, lead time…
- [Capacity terrain](https://beamline.io/components/capacity-terrain): A schedule that stands on a terrain of capacity: under each station's lane of jobs, every day's load rises to its capacity line and an overload is a…
- [Domino move](https://beamline.io/components/domino-move): A room board where one move shows its chain: drag a booking (or pick it and aim it with the keys) onto a taken room and the bookings it would push appear…
- [Match board](https://beamline.io/components/match-board): Month-end matching as one picture: bank lines and books entries side by side with threads between them — solid for matched, dashed with the host's reason…
- [Split anything](https://beamline.io/components/split-anything): One splitter for money, quantities and time: a bar of the whole shows every target's part and what is not split yet; items are dragged onto targets…
- [Approval route](https://beamline.io/components/approval-route): A policy you can read by trying it: an amount on a log scale with the thresholds as marks (the arrows stop just under, at and just over each), the…
- [Proposal twin](https://beamline.io/components/proposal-twin): A proposal edited beside the client's exact page, the two linked both ways: a field in focus lights its twin on the page, scrolls it into view and joins…
- [Quantity river](https://beamline.io/components/quantity-river): The life of one order line's quantity: at rest a bar of its real split ("5 delivered · 1 back in stock · 1 damaged · 5 cancelled"), unfolding into a…
- [Stock horizon](https://beamline.io/components/stock-horizon): Projected stock per item over the coming weeks: a strip of weeks with the safety line, weeks under safety in warn and short in danger, every dip said in…
- [Ageing queue](https://beamline.io/components/ageing-queue): Tickets that visibly age: each ticket's ring fills toward its own service target and steps from on time to late to very late, the words say when it is…
- [Import lens](https://beamline.io/components/import-lens): A messy import made fixable by kind: rows stream through the checks once with live counts; problems gather into groups that say what is wrong in words…
- [Duplicate guard](https://beamline.io/components/duplicate-guard): A new record that watches for the one that already exists: as you type, the likely duplicates slide in beside the form with the fields that match lit and…
- [Scan flow](https://beamline.io/components/scan-flow): A pick list that takes scans: each scan flies from the scan field into its row and fills that row's quantity ring, and the prompt moves to the next bin…
- [Pack planner](https://beamline.io/components/pack-planner): The packing bench: the order's items sit in box outlines drawn to scale, each box filling to its padding line, weighing against its limit and naming the…
- [Provenance lens](https://beamline.io/components/provenance-lens): Hold Alt (or press Lens) over any number on the page and see what makes it: hairline threads draw from the number to the rows, cards and documents on…
- [Record time machine](https://beamline.io/components/record-time-machine): The record itself, rewound in place: a slim track of every change (a tick per change in its person's colour, hollow for automation, named moments as…
- [Tension timeline](https://beamline.io/components/tension-timeline): A plan where slack is something you see: every dependency is a string from one task's end to the next one's start, sagging with the days it has to spare…
- [Event calendar](https://beamline.io/components/event-calendar): A schedule of events by day, week or month: events that overlap sit side by side, drag one to another time or day or pull its bottom edge to change its…
- [Kanban board](https://beamline.io/components/kanban-board): Work as cards in columns by stage: drag a card (or lift it with Space and walk it with the arrows) to another column or place, the cards around it make…
- [Data grid](https://beamline.io/components/data-grid): Virtualised local or server-driven grid: stable row IDs, manual sort and filters, pages or infinite loading, retry without losing rows, inline editing…
- [Pivot table](https://beamline.io/components/pivot-table): Records summed up by the fields people choose: rows grouped by one or more fields that fold open level by level, columns split by another, one or more…
- [Query builder](https://beamline.io/components/query-builder): Conditions people build by hand to pick out records: each rule is a field, an operator that fits the field (contains, between, in the last N days, is any…
- [Sort order](https://beamline.io/components/sort-order): Several sort keys in order. Each key says its rank in words ("Sort by Plan, A to Z", "then by Size, largest first") with its order named for the field's…
- [Metric rules](https://beamline.io/components/metric-rules): Ordered rules that colour a dashboard number. Each rule has a name for what its colour means ("At risk", not just red) and a tone (good, watch, act…
- [Log viewer](https://beamline.io/components/log-viewer): Log lines as they stream: it follows the newest line until you scroll up, then counts what arrived and brings you back in one click; filter by level with…
- [JSON viewer](https://beamline.io/components/json-viewer): A JSON value as a tree you can read and walk: branches open and close (one level, or all at once), closed ones preview what they hold, every value reads…
- [JSON diff viewer](https://beamline.io/components/json-diff-viewer): Two versions of a JSON value compared as one tree: added keys marked +, removed ones struck through with −, changed values read old → new, unchanged keys…
- [Import mapping](https://beamline.io/components/import-mapping): Map the columns of a file to the fields of one record type, see what every value becomes, then run the import and get an outcome for each row…
- [Diff table](https://beamline.io/components/diff-table): Proposed edits to a table before they happen: new rows green, removals struck through, changed cells old → new; each change can be left out and Apply…
- [Filter toolbar](https://beamline.io/components/filter-toolbar): The filters on a list kept in view: chips that remove themselves, Add filter with fields and their values, Clear all, and room for search or view controls.
- [Table](https://beamline.io/components/table): A native HTML table for small datasets, composed with children or driven by records; shared server paging, sorting, filters, search, stable-ID selection…

Part of [Beamline UI: polished, editable React components and complete screens for custom applications](https://beamline.io/). [Build your own style from any look; tokens, fonts, radii, motion; not only colours](https://beamline.io/docs/customize).
