Transcription

Displays timestamped speech segments, highlights playback time, and optionally seeks a player through accessible phrase controls.

Selected position: 0s · Transcript data example

Navigate timed speech

Selected position: 0s · Transcript data example

Each phrase has a supplied timestamp and optional speaker. This example updates a local position when selected. Connect onSeek to a media element and currentTime to playback to synchronize a real transcript.

Use it for

  • Supply stable segment ids, text and finite non-negative timestamps in seconds.
  • currentTime controls highlighting. Only activating a segment invokes onSeek; playback updates never trigger seek feedback loops.
  • Without onSeek the transcript is noninteractive text. Empty or invalid segments are omitted.
  • autoScroll is off by default. When enabled it follows active text until the reader scrolls or interacts, then offers Follow transcript.
  • CSS-only markup displays supplied text and states; synchronization requires application code.

Avoid

  • Treating supplied text as a live transcription service.
  • Forcing the viewport back to the active phrase after the reader scrolls away.

React package

npm install @noorddev/vlak-react

Load the stylesheet once at your app root, then import the components you use.

import "@noorddev/vlak-react/css";
import { Transcription } from "@noorddev/vlak-react";

Per-component imports are available at @noorddev/vlak-react/components/transcription.

Copy the source

npx @noorddev/vlak-cli add transcription

Adds the component and its dependencies to components/vlak/. Follow the StyleX setup to compile the source in your application.

shadcn registry

npx shadcn add https://vlak.dev/r/transcription.json

Installs the same source through the shadcn CLI.

"use client";
import { useRef, useState } from "react";
import { Transcription } from "@noorddev/vlak-react";

export function TimedTranscript({ src }: { src: string }) {
  const audioRef = useRef<HTMLAudioElement>(null);
  const [time, setTime] = useState(0);
  return <>
    <audio ref={audioRef} src={src} controls aria-label="Spoken brief" onTimeUpdate={event => setTime(event.currentTarget.currentTime)} />
    <Transcription currentTime={time} onSeek={seconds => {
      if (audioRef.current) audioRef.current.currentTime = seconds;
    }} segments={[
      { id: "opening", startSecond: 0, endSecond: 4, speaker: "Mina", text: "The brief is ready for review." },
      { id: "next", startSecond: 4, endSecond: 8, text: "Each decision has an owner and a next step." },
    ]} />
  </>;
}

See the integration example to compose these components with your application's model and data.

Transcription

Supplied timed text. Playback remains application-owned; only activation requests a seek.

PropTypeDefaultDescription
segmentsrequiredreadonly TranscriptionSegment[]
currentTimenumber0
onSeek(seconds: number) => void
labelstring"Transcript"
autoScrollbooleanfalseFollow active text until the reader scrolls. Off by default.
maxHeightstring | number"24rem"

Also accepts HTMLAttributes<HTMLDivElement>.

The ref reaches HTMLDivElement.

KeysDoes
TabReaches the transcript region and optional phrase seek controls
Arrow keys, Page Up, Page Down, Home, EndScrolls the focused region and pauses automatic following
Enter, SpaceSeeks to the focused phrase or resumes following
  • A named region contains an ordered list, timestamps and optional speaker labels.
  • Active text exposes aria-current and a full selected fill. Seek buttons have 44px minimum height and visible focus.
  • Following moves only this transcript's scroll position; it never steals focus or announces each playback tick.

The same styles are available as CSS. Native HTML provides the static presentation; React or application code supplies state updates and actions.

npm install @noorddev/vlak
import "@noorddev/vlak/css";
<div class="rs-transcription"><div class="rs-transcription-viewport" role="group" aria-label="Transcript" tabindex="0"><ol class="rs-transcription-list"><li><div class="rs-transcription-segment rs-transcription-active" aria-current="true"><span class="rs-transcription-time">0:00</span><span>The brief is ready for review.</span></div></li></ol></div></div>

Classes

.rs-transcription.rs-transcription-viewport.rs-transcription-list.rs-transcription-segment.rs-transcription-interactive.rs-transcription-active.rs-transcription-time.rs-transcription-speaker.rs-transcription-empty