agents-ui
componentagent-chat-transcriptsource: ok · hit
Agent Chat Transcript
A component for displaying the agent session's chat transcript.
npx shadcn add "https://livekit.com/ui/r/agent-chat-transcript.json"free
Where it goes
chat
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · light@livekit/components-react@^2.0.0streamdown
Registry dependencies
message-scrollermessagebubblemarker@agents-ui/agent-chat-indicator
Files1 · source cached 10/9/2026, 6:39:27 AM
components/agents-ui/agent-chat-transcript.tsx
'use client';
import { type ComponentProps } from 'react';
import { type AgentState, type ReceivedMessage } from '@livekit/components-react';
import { Streamdown } from 'streamdown';
import { Bubble, BubbleContent } from '@/components/ui/bubble';
import { Message, MessageContent } from '@/components/ui/message';
import { Marker, MarkerContent, MarkerIcon } from '@/components/ui/marker';
import {
MessageScroller,
MessageScrollerButton,
MessageScrollerContent,
MessageScrollerItem,
MessageScrollerProvider,
MessageScrollerViewport,
} from '@/components/ui/message-scroller';
import { AgentChatIndicator } from '@/components/agents-ui/agent-chat-indicator';
/**
* Props for the AgentChatTranscript component.
*/
export interface AgentChatTranscriptProps
extends
ComponentProps<'div'>,
ComponentProps<typeof MessageScrollerProvider>,
ComponentProps<typeof MessageScrollerViewport>,
ComponentProps<typeof MessageScrollerContent> {
/**
* Whether to automatically scroll to the bottom of the transcript when new messages are added.
* @defaultValue true
*/
autoScroll?: boolean;
/**
* The scroll anchor to use when auto-scrolling.
* @defaultValue false
*/
scrollAnchor?: boolean | 'user' | 'other' | 'any';
/**
* The scroll button render function
*/
scrollButtonRender?: ComponentProps<typeof MessageScrollerButton>['render'];
/**
* The scroll button behavior
*/
scrollButtonBehavior?: ComponentProps<typeof MessageScrollerButton>['behavior'];
/**
* The scroll button direction
*/
scrollButtonDirection?: ComponentProps<typeof MessageScrollerButton>['direction'];
/**
* The current state of the agent. When 'thinking', displays a loading indicator.
*/
agentState?: AgentState;
/**
* Array of messages to display in the transcript.
* @defaultValue []
*/
messages?: ReceivedMessage[];
/**
* Additional CSS class names to apply to the conversation container.
*/
className?: string;
}
/**
* A chat transcript component that displays a conversation between the user and agent.
* Shows messages with timestamps and origin indicators, plus a thinking indicator
* when the agent is processing.
*
* @extends ComponentProps<'div'>, ComponentProps<typeof MessageScrollerProvider>, ComponentProps<typeof MessageScrollerViewport>, ComponentProps<typeof MessageScrollerContent>
*
*
* @example
* ```tsx
* <AgentChatTranscript
* agentState={agentState}
* messages={chatMessages}
* />
* ```
*/
export function AgentChatTranscript({
scrollAnchor,
autoScroll = true,
scrollMargin,
scrollEdgeThreshold,
preserveScrollOnPrepend,
scrollPreviousItemPeek,
defaultScrollPosition = 'last-anchor',
scrollButtonRender,
scrollButtonBehavior,
scrollButtonDirection,
spacerClassName,
agentState,
messages = [],
className,
...props
}: AgentChatTranscriptProps) {
return (
<MessageScrollerProvider
autoScroll={autoScroll}
scrollMargin={scrollMargin}
defaultScrollPosition={defaultScrollPosition}
scrollEdgeThreshold={scrollEdgeThreshold}
scrollPreviousItemPeek={scrollPreviousItemPeek}
>
<MessageScroller className={className} {...props}>
<MessageScrollerViewport preserveScrollOnPrepend={preserveScrollOnPrepend}>
<MessageScrollerContent
spacerClassName={spacerClassName}
aria-busy={agentState === 'thinking'}
>
{messages.map((receivedMessage) => {
const { id, timestamp, from, message } = receivedMessage;
const time = new Date(timestamp);
const isUser = from?.isLocal;
const locale = typeof navigator !== 'undefined' ? navigator.language : 'en-US';
const title = time.toLocaleTimeString(locale, { timeStyle: 'full' });
let _scrollAnchor = false;
if (
scrollAnchor === 'any' ||
(scrollAnchor === 'user' && isUser) ||
(scrollAnchor === 'other' && !isUser)
) {
_scrollAnchor = true;
}
return (
<MessageScrollerItem key={id} messageId={id} scrollAnchor={_scrollAnchor}>
<Message align={isUser ? 'end' : 'start'} title={title}>
<MessageContent>
<Bubble
align={isUser ? 'end' : 'start'}
variant={isUser ? 'secondary' : 'ghost'}
>
<BubbleContent>
<Streamdown>{message}</Streamdown>
</BubbleContent>
</Bubble>
</MessageContent>
</Message>
</MessageScrollerItem>
);
})}
{/* Agent is thinking indicator */}
{agentState === 'thinking' && (
<MessageScrollerItem>
<Marker role="status">
<MarkerIcon>
<AgentChatIndicator size="sm" />
</MarkerIcon>
<MarkerContent className="shimmer">Thinking...</MarkerContent>
</Marker>
</MessageScrollerItem>
)}
</MessageScrollerContent>
</MessageScrollerViewport>
{/* Scroll to bottom button */}
<MessageScrollerButton
variant="outline"
className="rounded-full"
render={scrollButtonRender}
behavior={scrollButtonBehavior}
direction={scrollButtonDirection}
/>
</MessageScroller>
</MessageScrollerProvider>
);
}