can you access the transcript from the react clien...
# ask-ai
w
can you access the transcript from the react client sdk?
t
Thanks for reaching out, Mohammad. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — you’re asking whether you can access the transcript from the React Client SDK. Yes — you can. Use the
useTranscriptions
hook from
@livekit/components-react
to receive real-time transcription segments published to the
lk.transcription
text-stream topic. The hook returns structured transcription segments (including participant identity, text, and whether segments are interim or final) that are ready to render in your UI. Example:
Copy code
import { useTranscriptions } from '@livekit/components-react';

function ChatDisplay() {
  const transcriptions = useTranscriptions();

  return (
    <div>
      {transcriptions.map((segment) => (
        <div key={segment.id}>
          <strong>{segment.participant?.identity}:</strong> {segment.text}
        </div>
      ))}
    </div>
  );
}
A couple of quick notes to ensure it works: • The room or agent must publish transcriptions to the
lk.transcription
topic (e.g., an STT agent or speech-to-text integration). • The hook differentiates interim vs final segments so you can display live updates and finalize text when appropriate. If you want, I can: • Show how to filter by participant identity or only show final segments. • Share an example that debounces interim updates to reduce UI flicker. Which of those would be most helpful? Sources: Text and transcriptions | LiveKit Documentation | Client SDKs | livekit/livekit_composite | DeepWiki | How to build an agent with speech-to-text input and text-only output | Live...
w
yes
t
Great — I’ll show two practical examples you can use with `useTranscriptions`: one that filters by participant identity and one that only shows final segments (plus a small debounce for interim updates to reduce UI flicker). I’ll also point out where to tweak behavior. 1) Filter by participant identity • Use this when you only want transcripts from a specific participant (by
identity
).
Copy code
import React from 'react';
import { useTranscriptions } from '@livekit/components-react';

function ParticipantTranscripts({ identity }: { identity: string }) {
  const transcriptions = useTranscriptions();

  const filtered = transcriptions.filter(
    (seg) => seg.participant?.identity === identity
  );

  return (
    <div>
      {filtered.map((seg) => (
        <div key={seg.id}>
          <strong>{seg.participant?.identity}:</strong> {seg.text}
        </div>
      ))}
    </div>
  );
}
2) Show only final segments and debounce interim updates • Use this when you want a stable UI and avoid flashing interim text. This example displays final segments immediately and updates interim text with a short debounce.
Copy code
import React, { useEffect, useMemo, useState } from 'react';
import { useTranscriptions } from '@livekit/components-react';

function FinalOnlyWithDebouncedInterim({ debounceMs = 300 }) {
  const transcriptions = useTranscriptions();

  // Store finalized segments separately
  const finalized = useMemo(
    () => transcriptions.filter((s) => s.isFinal),
    [transcriptions]
  );

  // Track last interim per participant and show them with debounce
  const [interimMap, setInterimMap] = useState<Record<string, string>>({});

  useEffect(() => {
    let timers: Record<string, number> = {};

    transcriptions.forEach((seg) => {
      if (!seg.isFinal) {
        const id = seg.participant?.identity ?? seg.id;
        // clear existing timer for this identity
        if (timers[id]) {
          window.clearTimeout(timers[id]);
        }
        // schedule update after debounceMs
        timers[id] = window.setTimeout(() => {
          setInterimMap((m) => ({ ...m, [id]: seg.text }));
        }, debounceMs);
      } else {
        // remove interim display when final arrives
        const id = seg.participant?.identity ?? seg.id;
        setInterimMap((m) => {
          const copy = { ...m };
          delete copy[id];
          return copy;
        });
      }
    });

    // cleanup timers on unmount or re-run
    return () => {
      Object.values(timers).forEach((t) => window.clearTimeout(t));
    };
  }, [transcriptions, debounceMs]);

  return (
    <div>
      <div>
        {finalized.map((seg) => (
          <div key={seg.id}>
            <strong>{seg.participant?.identity}:</strong> {seg.text}
          </div>
        ))}
      </div>

      <div style={{ opacity: 0.7 }}>
        {Object.entries(interimMap).map(([identity, text]) => (
          <div key={identity}>
            <strong>{identity} (listening):</strong> {text}
          </div>
        ))}
      </div>
    </div>
  );
}
Notes and tips • The hook listens to the
lk.transcription
text stream; ensure your agent or STT integration publishes there. • Use
seg.participant?.identity
to attribute segments; fallback to
seg.id
if identity is missing. • Adjust
debounceMs
to balance responsiveness vs UI stability. • If you need to persist transcripts, collect the segments into state or send them to your backend when
seg.isFinal
is true. Would you like a ready-to-drop example that also saves final segments to local state or uploads them to a server? Which option fits your use case: single-speaker transcription, multi-speaker with attribution, or saving transcripts for later?