> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daily.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling events

> Subscribe to daily-js events in React with useDailyEvent and useThrottledDailyEvent, including the memoized-callback rule.

In Daily React you rarely subscribe to events directly, because most call state already has a hook. When you do need a raw event, [`useDailyEvent`](/reference/daily-react/use-daily-event) and [`useThrottledDailyEvent`](/reference/daily-react/use-throttled-daily-event) register and tear down listeners for you.

## Reach for a hook first

Before subscribing to an event, check whether a hook already exposes the state it carries. The hooks keep the state for you and re-render on change, which is usually what you actually want:

| Event                                                           | Hook                                                                        |
| --------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `participant-joined`, `participant-left`, `participant-updated` | [`useParticipantIds`](/reference/daily-react/use-participant-ids)           |
| `active-speaker-change`                                         | [`useActiveSpeakerId`](/reference/daily-react/use-active-speaker-id)        |
| `app-message`                                                   | [`useAppMessage`](/reference/daily-react/use-app-message)                   |
| `recording-started`, `recording-stopped`, `recording-error`     | [`useRecording`](/reference/daily-react/use-recording)                      |
| `live-streaming-*`                                              | [`useLiveStreaming`](/reference/daily-react/use-live-streaming)             |
| `transcription-*`                                               | [`useTranscription`](/reference/daily-react/use-transcription)              |
| `network-quality-change`, `network-connection`                  | [`useNetwork`](/reference/daily-react/use-network)                          |
| `error`, `nonfatal-error`                                       | [`useDailyError`](/reference/daily-react/use-daily-error)                   |
| `waiting-participant-*`                                         | [`useWaitingParticipants`](/reference/daily-react/use-waiting-participants) |

Many of these hooks also accept the corresponding `on...` callback, so you can run side effects on the event while still getting the reactive state.

## Subscribe with useDailyEvent

For events without a dedicated hook, [`useDailyEvent`](/reference/daily-react/use-daily-event) registers a listener and removes it when the component unmounts:

```jsx theme={null}
import { useCallback, useState } from 'react';
import { useDailyEvent } from '@daily-co/daily-react';

function JoinStatus() {
  const [joined, setJoined] = useState(false);

  useDailyEvent(
    'joined-meeting',
    useCallback(() => setJoined(true), [])
  );

  return <span>{joined ? 'In the call' : 'Not joined'}</span>;
}
```

### The callback must be memoized

This is the one rule that trips people up. The `callback` you pass must be a stable reference, normally wrapped in [`useCallback`](https://react.dev/reference/react/useCallback):

```jsx theme={null}
// Correct: stable reference
useDailyEvent('left-meeting', useCallback(() => cleanup(), []));

// Wrong: a new function every render
useDailyEvent('left-meeting', () => cleanup());
```

<Warning>
  Passing a fresh function each render makes the hook resubscribe on every render, which can trigger a console error about a potential re-render loop. Always memoize the callback, and list its real dependencies in the `useCallback` array.
</Warning>

## Batch high-frequency events

Some events fire in bursts, for example a wave of `participant-joined` events when a large call starts. [`useThrottledDailyEvent`](/reference/daily-react/use-throttled-daily-event) collects events over a window and hands you an **array** of everything that happened, so you update state once instead of once per event:

```jsx theme={null}
import { useCallback, useState } from 'react';
import { useThrottledDailyEvent } from '@daily-co/daily-react';

function ParticipantCounter() {
  const [count, setCount] = useState(0);

  useThrottledDailyEvent(
    ['participant-joined', 'participant-left'],
    useCallback((events) => {
      setCount((current) => {
        let next = current;
        for (const event of events) {
          next += event.action === 'participant-joined' ? 1 : -1;
        }
        return next;
      });
    }, [])
  );

  return <span>{count} in call</span>;
}
```

The default throttle window is 100ms; pass `throttleTimeout` to change it. The same memoized-callback rule applies. Note that the callback receives an array, unlike `useDailyEvent`, which receives a single event.

## Choosing between them

* One-off or low-frequency event, react immediately: [`useDailyEvent`](/reference/daily-react/use-daily-event).
* Bursty or high-frequency event where you only need the net result: [`useThrottledDailyEvent`](/reference/daily-react/use-throttled-daily-event).
* The event maps to call state you want to render: use the dedicated hook from the table above.

## Next steps

<CardGroup cols={2}>
  <Card title="Custom messages" icon="message" href="/docs/daily-react/docs/app-messages">
    Send and receive your own data with useAppMessage.
  </Card>

  <Card title="Events reference" icon="bolt" href="/reference/daily-js/events">
    Every daily-js event and its payload.
  </Card>
</CardGroup>
