usePipecatEventStream
Hook to subscribe to Pipecat RTVI events with throttled updates and optional grouping.
Overview
usePipecatEventStream
subscribes to Pipecat RTVI events and exposes a throttled snapshot of recent events. It minimizes re-renders during high-frequency event bursts and can optionally group consecutive events by a key.
Usage
Basic
import { usePipecatEventStream } from "@pipecat-ai/voice-ui-kit";
export function RecentEvents() {
const { events, clear } = usePipecatEventStream({ maxEvents: 100 });
return (
<div>
<button onClick={clear}>Clear</button>
<ul>
{events.map((e) => (
<li key={e.id}>
<strong>{e.type}</strong>
<span> – {e.timestamp.toLocaleTimeString()}</span>
</li>
))}
</ul>
</div>
);
}
Filter and Throttle
const { events } = usePipecatEventStream({
includeEvents: ["serverMessage", "serverResponse"],
throttleMs: 100, // batch UI updates every 100ms
});
Group Consecutive
const { groups } = usePipecatEventStream({
groupConsecutive: true,
groupKey: (e) => e.type,
});
Full example
import React, { useEffect, useRef, useState } from "react";
import {
usePipecatEventStream,
type PipecatEventGroup,
} from "@pipecat-ai/voice-ui-kit";
export function EventStreamExample() {
const { events, groups } = usePipecatEventStream({
maxEvents: 200,
groupConsecutive: true,
});
const [expanded, setExpanded] = useState<Set<string>>(new Set());
const endRef = useRef<HTMLDivElement>(null);
// Auto-scroll on new events
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: "smooth" });
}, [events]);
const toggleGroup = (id: string) => {
setExpanded((prev) => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
};
return (
<div style={{ height: 300, overflow: "auto", fontFamily: "monospace", fontSize: 12 }}>
{groups.map((group: PipecatEventGroup) => {
const isExpanded = expanded.has(group.id);
const hasMultiple = group.events.length > 1;
if (!hasMultiple || isExpanded) {
return group.events.map((e, idx) => (
<div key={e.id}>
{hasMultiple && idx === 0 ? (
<button onClick={() => toggleGroup(group.id)}>[−]</button>
) : (
<span>•</span>
)}
<span> [{e.timestamp.toLocaleTimeString()}] </span>
<strong>{e.type}</strong> {e.data ? JSON.stringify(e.data) : "null"}
</div>
));
}
return (
<div key={group.id}>
<button onClick={() => toggleGroup(group.id)}>[+]</button>
<span> [{group.events[0].timestamp.toLocaleTimeString()}] </span>
<strong>{group.type}</strong> ({group.events.length} events)
</div>
);
})}
<div ref={endRef} />
</div>
);
}
API
Options
Prop | Type | Default |
---|---|---|
maxEvents? | number | 500 |
ignoreEvents? | string[] | [RTVIEvent.LocalAudioLevel] |
includeEvents? | string[] | undefined |
paused? | boolean | false |
mapEventData? | (data: unknown, eventType: string) => unknown | undefined |
throttleMs? | number | 0 |
onEvent? | (event: PipecatEventLog) => void | undefined |
groupConsecutive? | boolean | false |
groupKey? | (event: PipecatEventLog) => string | (e) => e.type |
Return
Prop | Type | Default |
---|---|---|
events | ReadonlyArray<PipecatEventLog> | - |
groups | ReadonlyArray<PipecatEventGroup> | - |
clear | () => void | - |