---
title: "Event stream"
description: "useEventStream subscribes to workspace events and invalidates queries automatically."
section: "Libraries"
group: "React"
---

# Event stream

`useEventStream` opens a WebSocket to the workspace event hub and debounces cache invalidations so your queries stay fresh in near real-time. In most cases, adding it once at the top of your app is all you need.

## Basic — auto-invalidate all caches

```tsx
import { useEventStream } from "@aeontel/react";

function WorkspaceShell({ workspaceId, children }: Props) {
  const { connected } = useEventStream(workspaceId);
  return (
    <>
      <ConnectionPill connected={connected} />
      {children}
    </>
  );
}
```

Every event routes to the matching entity's key factory and calls `invalidateQueries` — `agent.updated` invalidates `agentKeys.all()`, `run.completed` invalidates `runKeys.all()`, and so on. Invalidations batch on a 500ms debounce to avoid thrash.

## With callbacks and filters

```tsx
useEventStream(workspaceId, {
  filter: ["run.completed", "agent.*"], // glob patterns
  onEvent: (event) => {
    if (event.type === "run.completed")
      toast.success(`Run ${event.subjectId} done`);
  },
});
```

The filter is server-side — only matching events are sent over the wire.

## Disabling

Pass `undefined` as the workspace id to keep the hook mounted but inactive (e.g. while the user is on a workspace-less route):

```tsx
const { connected } = useEventStream(currentWorkspaceId ?? undefined);
```

## Polling fallback

When `connected` is true, you can drop list polling to a 60s safety net. See [`getRefetchInterval`](/react/polling) for the pattern.
