Activity feed
Activity feeds are bursty: a build kicks off and emits twelve events in 200ms, then nothing for two seconds. Two failure modes to avoid:
- No batching — one render per event tanks the list during a burst.
- Batching with only flushMs — the last events of a burst hang in the buffer waiting for the next interval tick. Users see the feed pause mid-burst.
flushMs: 80, idleMs: 40 coalesces the
steady-state work and trims the tail.
Live demo
Activity feed
idlerenders: 1events: 0
Bursty server: 4–12 events arrive back-to-back, then a 1.5–3s pause. flushMs: 80, idleMs: 40 — the batch coalesces the burst into one render, and idleMs flushes the tail without waiting for the next tick.
Press Start. Events will stream in as bursts.
Source
Feed.tsx
type TFeedEvent = {
type: "feed";
id: string;
actor: string;
actorKind: "human" | "agent";
verb: string;
target: string;
ts: number;
};
const MAX_VISIBLE = 60;
function ActivityFeed() {
const [items, setItems] = useState<TFeedEvent[]>([]);
useSocketEventBatch(
manager,
"feed",
(msgs) => {
if (msgs.length === 0) return;
setItems((prev) => [...msgs, ...prev].slice(0, MAX_VISIBLE));
},
{ flushMs: 80, idleMs: 40 },
);
// ...render
}Mixing human and agent activity
The protocol carries an actorKind so the
feed can render agent events differently from human events without
the client guessing. Same wire shape, two render paths.
See Backpressure
for the full flushMs /
idleMs reference.