Event Stream

A live tail of analytics events with their payloads — the tool for debugging the tracking itself. Autoscroll pauses when you scroll away and the list is capped rather than unbounded.

src/components/ui/event-stream.tsx

About

From the component's own source, where it was written.

A live tail of analytics events, newest first.

**The debugging tool for the tracking itself.** Funnels and dashboards show what was recorded; this shows what is *arriving*, right now, with its properties — which is the only way to answer "did that click fire the event, and did it carry the right payload" without opening a network panel.

**Autoscroll pauses when you scroll away, and resumes at the top.** A stream that yanks you back to the newest row every time one arrives makes reading any event impossible — the thing you were looking at is gone before you finish. Scroll position is the intent signal, so no toggle is needed for the common case, and the explicit pause button exists for when the rate is high enough that even hovering is a fight.

**The list is capped and drops the oldest.** An unbounded live list is a memory leak with a UI: at a few hundred events per minute a tab left open over lunch is tens of thousands of nodes. limit is the ceiling, and the count of what was dropped is shown rather than silently forgotten.

Rendering is newest-first with aria-live="off" deliberately: announcing every arriving event would make a screen reader unusable. The count is announced politely instead.

Install

npx astralyx-ui add event-stream

Usage

import { EventStream } from '@/components/ui/event-stream'

<EventStream events={events} limit={500} />

Composer

Change the props and the source below follows.

Event stream

9
Props
tsx
<EventStream
  events={events}
  filterable={true}
  height={280}
/>

Examples

A tail, with payloads

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

EventStream

Extends Omit<ComponentProps<'div'>, 'onSelect'>.

PropTypeDefaultDescription
eventsStreamEvent[]required{ id, name, at, properties?, kind?, user? }. Newest first.
limitnumber500An unbounded live list is a memory leak with a UI. Dropped rows are counted, not silently forgotten.
heightnumber360—
filterablebooleantrueFilter box over event names.
onSelect(event: StreamEvent) => void——
colorFor(kind: string) => string—Kinds mapped to a CSS colour.
pausedboolean——
onPausedChange(paused: boolean) => void——
timeFormat(date: Date) => stringDEFAULT_TIME—
emptyLabelstring'Waiting for events…'—
filterPlaceholderstring'Filter by name'—
labelstring'Event stream'—
isthe tracking—Funnels show what was recorded; this shows what is arriving, with its payload — the only way to answer "did that click fire the event, and was it right" without a network panel.

Types

StreamEvent

A live tail of analytics events, newest first.

FieldTypeDescription
idstring—
namestring—
atDate | string—
properties?Record<string, unknown>Shown expanded under the row.
kind?stringColours the dot — 'error', 'page', whatever you group by.
user?ReactNode—

Behaviour

TopicDetailDescription
autoscrollscroll is the intentA stream that yanks you to the newest row makes reading any event impossible. It follows only while you are already at the top.
aria-liveoff, deliberatelyAnnouncing every arriving event makes a screen reader unusable. The count is announced politely instead.