> ## 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.

# Custom messages

> Send and receive arbitrary data between participants in React with useAppMessage, for chat, reactions, and app state.

App messages are Daily's general-purpose data channel: arbitrary JSON sent between participants, separate from audio and video. Use them for chat, emoji reactions, "raise hand," shared cursors, or any custom signaling. [`useAppMessage`](/reference/daily-react/use-app-message) wraps both sending and receiving in one hook.

## Send and receive

`useAppMessage` returns a `sendAppMessage` function, and takes an `onAppMessage` callback for incoming messages:

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

function Chat() {
  const [messages, setMessages] = useState([]);

  const sendAppMessage = useAppMessage({
    onAppMessage: useCallback((event) => {
      // event.fromId is the sender's session ID; event.data is your payload.
      setMessages((prev) => [...prev, { from: event.fromId, ...event.data }]);
    }, []),
  });

  const send = (text) => sendAppMessage({ text }, '*');

  return (
    <div>
      <ul>
        {messages.map((m, i) => (
          <li key={i}>{m.from.slice(0, 6)}: {m.text}</li>
        ))}
      </ul>
      <ChatInput onSend={send} />
    </div>
  );
}
```

The `onAppMessage` callback must be memoized with `useCallback`, like all Daily React event callbacks.

## Addressing messages

The second argument to `sendAppMessage` chooses recipients:

* `'*'` sends to every other participant (the default broadcast).
* A `session_id` string sends to one participant only.

```jsx theme={null}
sendAppMessage({ type: 'reaction', emoji: '👏' }, '*');      // everyone
sendAppMessage({ type: 'private', text: 'hi' }, theirSessionId); // one person
```

<Note>
  App messages are not delivered to the sender. If you want the sender's own UI to reflect the message (an optimistic chat bubble, your own reaction), update local state when you call `sendAppMessage`, not from `onAppMessage`.
</Note>

## Replying from the handler

`onAppMessage` receives `sendAppMessage` as its second argument, so you can respond to a message without calling the hook twice or threading the function through closures:

```jsx theme={null}
useAppMessage({
  onAppMessage: useCallback((event, sendAppMessage) => {
    if (event.data?.type === 'ping') {
      sendAppMessage({ type: 'pong' }, event.fromId);
    }
  }, []),
});
```

## Patterns

Use a `type` field to multiplex different kinds of messages over the one channel:

```jsx theme={null}
const sendAppMessage = useAppMessage({
  onAppMessage: useCallback((event) => {
    switch (event.data?.type) {
      case 'chat':
        addChatMessage(event.fromId, event.data.text);
        break;
      case 'reaction':
        showReaction(event.fromId, event.data.emoji);
        break;
      case 'raise-hand':
        toggleRaisedHand(event.fromId, event.data.raised);
        break;
    }
  }, []),
});
```

<Warning>
  App messages are for transient signaling, not durable state, and have a per-message size limit. Do not send large blobs (images, files); send a URL instead. For persisting shared call state, see [`useMeetingSessionState`](/reference/daily-react/use-meeting-session-state).
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Handling events" icon="bolt" href="/docs/daily-react/docs/events">
    The event model behind the callbacks.
  </Card>

  <Card title="sendAppMessage reference" icon="code" href="/reference/daily-js/instance-methods/send-app-message">
    The underlying daily-js method and limits.
  </Card>
</CardGroup>
