usePipecatConversation
Hook that derives a clean, ordered conversation stream from RTVI events.
Overview
usePipecatConversation provides access to conversation state managed by the @pipecat-ai/client-react PipecatClientProvider. The kit re-exports the upstream hook for convenience. The provider handles all Pipecat RTVI events to produce a clean message list suitable for rendering a chat transcript. It handles streaming text, placeholder messages, finalization, and merging of consecutive messages from the same role.
Setup
Conversation state is automatically managed by the PipecatClientProvider when you use PipecatAppBase, so no additional setup is required:
import { PipecatAppBase, usePipecatConversation } from "@pipecat-ai/voice-ui-kit";
export default function App() {
return (
<PipecatAppBase
transportType="smallwebrtc"
connectParams={{ webrtcUrl: "/api/offer" }}
>
<YourApp />
</PipecatAppBase>
);
}Usage
Basic
import { usePipecatConversation } from "@pipecat-ai/voice-ui-kit";
export function ConversationList() {
const { messages } = usePipecatConversation();
return (
<ul>
{messages.map((m, i) => (
<li key={i}>
<strong>{m.role}:</strong>{" "}
{m.parts.map((part, idx) => {
if (part.text && typeof part.text === "object" && "spoken" in part.text) {
return <span key={idx}>{part.text.spoken + part.text.unspoken}</span>;
}
return <span key={idx}>{String(part.text)}</span>;
})}
</li>
))}
</ul>
);
}With lifecycle callbacks
const { messages } = usePipecatConversation({
onMessageCreated: (message) => console.log("New message:", message),
onMessageUpdated: (message) => console.log("Message updated:", message),
});Custom aggregation metadata (inline vs block, spoken vs not spoken)
Only "word" and "sentence" are considered default aggregation types. Any other aggregatedBy value is custom and arbitrary, and should be configured through aggregationMetadata (and optionally botOutputRenderers in your UI components).
This metadata influences:
- Rendering: whether an aggregation should be treated as inline text or a block (e.g. code blocks)
- Karaoke-style highlighting: whether the aggregation is expected to be spoken (
isSpoken)
const { messages } = usePipecatConversation({
aggregationMetadata: {
code: { displayMode: "block", isSpoken: false },
credit_card: { displayMode: "inline", isSpoken: false },
},
});Notes:
displayMode: "block"parts are rendered on their own line byMessageContent.- For cleaner rendering, non-spoken block parts are trimmed of leading/trailing whitespace before display (the stored text is not modified).
Full example
import React, { useEffect, useRef } from "react";
import { usePipecatConversation } from "@pipecat-ai/voice-ui-kit";
export function ConversationExample() {
const { messages } = usePipecatConversation();
const endRef = useRef<HTMLDivElement>(null);
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
return (
<div style={{ height: 300, overflow: "auto", fontFamily: "monospace", fontSize: 12 }}>
{messages.map((m, i) => (
<div key={i}>
<strong>[{m.role}]</strong>{" "}
{m.parts.map((part, idx) => {
if (part.text && typeof part.text === "object" && "spoken" in part.text) {
return <span key={idx}>{part.text.spoken + part.text.unspoken}</span>;
}
return <span key={idx}>{String(part.text)}</span>;
})}
<span style={{ opacity: 0.6, marginLeft: 8 }}>
{new Date(m.createdAt).toLocaleTimeString()}
</span>
</div>
))}
<div ref={endRef} />
</div>
);
}Injecting messages
You can inject messages directly via the hook, or by using ConsoleTemplate callbacks.
import React from "react";
import { usePipecatConversation } from "@pipecat-ai/voice-ui-kit";
export function InjectViaHook() {
const { injectMessage } = usePipecatConversation();
return (
<button
onClick={() =>
injectMessage({
role: "system",
parts: [{ text: "Hello from the system", final: true, createdAt: new Date().toISOString() }],
createdAt: new Date().toISOString()
})
}
>
Inject message
</button>
);
}Changelog
- v0.10.0
- Implementation moved to
@pipecat-ai/client-react. The kit now re-exports the upstream hook.ConversationProvideris no longer required.
- Implementation moved to
- v0.3.0
- Added. Derives a clean, ordered conversation stream from RTVI events for chat-style transcripts.