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

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Add toasts

> Render toast popups for new inbox messages and handle clicks on the web SDKs.

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

Toasts share the inbox's feed and <Doc href="/docs/in-app/authenticate-users">authentication</Doc>. This page only covers rendering the toast component.

**Try it live:**

<Card title="Toast demo" icon="play" href="https://inbox-demo.courier.com/inbox-demo?layout=courier-toast">
  See toasts pop up for new messages.
</Card>

Toasts are transient popups that appear when a new message arrives. Render them on their own or alongside the inbox.

<Frame caption="A Courier toast appearing for a new message">
  <img src="https://mintcdn.com/courier-4f1f25dc/rYENcCCTyPPrDtw0/assets/toast-default.webp?fit=max&auto=format&n=rYENcCCTyPPrDtw0&q=85&s=d8dde1dc885025b113f253dc1b0518c2" alt="A Courier toast for a newly delivered message, with the message title, body, and Accept and Pass actions" className="mx-auto" width="3152" height="1776" data-path="assets/toast-default.webp" />
</Frame>

<Note>
  The built-in toast is web-only. On iOS, Android, Flutter, and React Native, listen for new messages and show your own UI (mobile tabs below). Or use <Doc href="/docs/integrations/push/apple-push-notification">push notifications</Doc>.
</Note>

## Set up

Install the SDK for your framework:

<CodeGroup>
  ```bash React theme={null}
  npm install @trycourier/courier-react
  # On React 17? Use @trycourier/courier-react-17 instead (identical API):
  # npm install @trycourier/courier-react-17
  ```

  ```bash Web Components theme={null}
  npm install @trycourier/courier-ui-toast
  ```

  ```bash Vue theme={null}
  npm install @trycourier/courier-vue
  ```

  ```bash Angular theme={null}
  npm install @trycourier/courier-angular
  ```

  ```bash iOS theme={null}
  # Swift Package Manager: add https://github.com/trycourier/courier-ios
  # or CocoaPods:
  pod 'Courier_iOS'
  ```

  ```bash Android theme={null}
  # In settings.gradle add the JitPack repo, then the dependency in build.gradle:
  # maven { url 'https://jitpack.io' }
  # implementation 'com.github.trycourier:courier-android:<version>'
  ```

  ```bash Flutter theme={null}
  flutter pub add courier_flutter
  ```

  ```bash React Native theme={null}
  npm install @trycourier/courier-react-native
  # then, for iOS:
  cd ios && pod install
  ```
</CodeGroup>

On mobile, the SDK also needs native project setup before it renders: <Doc href="/docs/sdk-libraries/ios#installation">iOS</Doc>, <Doc href="/docs/sdk-libraries/android#installation">Android</Doc>, <Doc href="/docs/sdk-libraries/flutter#installation">Flutter</Doc>, and <Doc href="/docs/sdk-libraries/react-native#installation">React Native</Doc>.

Then <Doc href="/docs/in-app/authenticate-users">authenticate the user</Doc> with the same `signIn` as the inbox and render the component. The web SDKs use the built-in `CourierToast`. On mobile, listen for new messages and show your own UI:

<CodeGroup>
  ```jsx React theme={null}
  import { CourierToast } from "@trycourier/courier-react";  // or "@trycourier/courier-react-17"

  export default function App() {
    // Renders toasts for new messages on the authenticated user's feed
    return <CourierToast />;
  }
  ```

  ```html Web Components theme={null}
  <courier-toast></courier-toast>

  <script type="module">
    import { Courier } from "@trycourier/courier-ui-toast";

    Courier.shared.signIn({ userId, jwt });
  </script>
  ```

  ```vue Vue theme={null}
  <script setup lang="ts">
  import { CourierToast } from "@trycourier/courier-vue";
  </script>

  <template>
    <CourierToast />
  </template>
  ```

  ```ts Angular theme={null}
  import { Component } from "@angular/core";
  import { CourierToastComponent } from "@trycourier/courier-angular";

  @Component({
    selector: "app-toast",
    standalone: true,
    imports: [CourierToastComponent],
    template: `<courier-toast></courier-toast>`,
  })
  export class ToastComponent {}
  ```

  ```swift iOS theme={null}
  // No built-in toast on iOS: listen for new messages and show your own UI
  let listener = await Courier.shared.addInboxListener(
    onMessageEvent: { message, index, feed, event in
      if event == .added {
        showMyToast(message) // your own toast UI
      }
    }
  )
  ```

  ```kotlin Android theme={null}
  // No built-in toast on Android: listen and show your own UI
  // addInboxListener is a suspend function; call it from a coroutine
  lifecycleScope.launch {
    val listener = Courier.shared.addInboxListener(
      onMessageEvent = { message, index, feed, event ->
        if (event == InboxMessageEvent.ADDED) {
          showMyToast(message) // your own toast UI
        }
      }
    )
  }
  ```

  ```dart Flutter theme={null}
  // No built-in toast on Flutter: listen and show your own UI
  final listener = await Courier.shared.addInboxListener(
    onMessageEvent: (message, index, feed, event) {
      if (event == InboxMessageEvent.added) {
        showMyToast(message); // your own toast UI
      }
    },
  );
  ```

  ```jsx React Native theme={null}
  // No built-in toast on React Native: listen and show your own UI
  const listener = await Courier.shared.addInboxListener({
    onMessageEvent: (message, index, feed, event) => {
      if (event === "added") {
        showMyToast(message); // your own toast UI
      }
    },
  });
  ```
</CodeGroup>

Full SDK references: <Doc href="/docs/sdk-libraries/courier-react-web">React</Doc>, <Doc href="/docs/in-app/web-components">Web Components</Doc>, <Doc href="/docs/sdk-libraries/courier-vue-web">Vue</Doc>, <Doc href="/docs/sdk-libraries/courier-angular-web">Angular</Doc>, <Doc href="/docs/sdk-libraries/ios">iOS</Doc>, <Doc href="/docs/sdk-libraries/android">Android</Doc>, <Doc href="/docs/sdk-libraries/flutter">Flutter</Doc>, <Doc href="/docs/sdk-libraries/react-native">React Native</Doc>.

## Handle clicks

A toast fires two events: one when the body is clicked, and one for each <Doc href="/docs/design/templates/design-studio#buttons">action button</Doc> the message carries. Action buttons do nothing by default. You decide where they go.

<Frame caption="A toast with action buttons. Each button raises its own click event.">
  <img src="https://mintcdn.com/courier-4f1f25dc/fN1MbwzHtG3Vhos5/assets/courier-toast-action-buttons.webp?fit=max&auto=format&n=fN1MbwzHtG3Vhos5&q=85&s=483d244496cf995bd58018ab1eeea56b" alt="A Courier toast notification with two action buttons below the message body" width="552" data-path="assets/courier-toast-action-buttons.webp" />
</Frame>

<CodeGroup>
  ```jsx React theme={null}
  <CourierToast
    onToastItemClick={({ message }) => {
      console.log("Toast clicked:", message);
    }}
    onToastItemActionClick={({ message, action }) => {
      window.open(action.href);
    }}
  />
  ```

  ```html Web Components theme={null}
  <courier-toast id="my-toast"></courier-toast>

  <script type="module">
    import { Courier, CourierToastDatastore } from "@trycourier/courier-ui-toast";

    const toast = document.getElementById("my-toast");

    toast.onToastItemClick(({ message }) => {
      window.open(message.actions[0].href);
    });

    toast.onToastItemActionClick(({ message, action }) => {
      window.open(action.href);
      CourierToastDatastore.shared.removeMessage(message);
    });
  </script>
  ```

  ```vue Vue theme={null}
  <CourierToast
    :on-toast-item-click="({ message }) => console.log('Toast clicked:', message)"
    :on-toast-item-action-click="({ action }) => window.open(action.href)"
  />
  ```

  ```ts Angular theme={null}
  // <courier-toast (toastItemClick)="onClick($event)"
  //                (toastItemActionClick)="onActionClick($event)"></courier-toast>

  onClick({ message }: CourierToastItemClickEvent) { /* ... */ }
  onActionClick({ action }: CourierToastItemActionClickEvent) { window.open(action.href); }
  ```
</CodeGroup>

Both events carry the `InboxMessage`. The action event also carries the clicked `InboxAction`. Every callback the component accepts is in <Doc href="/docs/in-app/add-toasts#props">Props</Doc> below.

<Note>
  Mobile has no built-in toast. Handle clicks in the UI you render from the inbox listener above.
</Note>

## Props

`<CourierToast />` in React, Vue, and Angular. The web component takes the same values as
kebab-case attributes, and `courier-toast` is the element name.

| Prop                     | Type                                         | Default                                                                       | Description                                                                                  |
| ------------------------ | -------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `lightTheme`             | `CourierToastTheme`                          |                                                                               | Theme applied in light mode.                                                                 |
| `darkTheme`              | `CourierToastTheme`                          |                                                                               | Theme applied in dark mode.                                                                  |
| `mode`                   | `CourierComponentThemeMode`                  | `"system"`                                                                    | Force light or dark instead of following the system.                                         |
| `autoDismiss`            | boolean                                      | `false`                                                                       | Dismiss each toast on a timer, with a countdown bar across the top.                          |
| `autoDismissTimeoutMs`   | number                                       | `5000`                                                                        | How long a toast stays up when `autoDismiss` is on.                                          |
| `dismissButton`          | `"visible"`, `"hidden"`, `"hover"`, `"auto"` | `"auto"`                                                                      | When the close button shows.                                                                 |
| `onToastItemClick`       | `(props) => void`                            |                                                                               | Called when a toast is clicked.                                                              |
| `onToastItemActionClick` | `(props) => void`                            |                                                                               | Called when a toast's action button is clicked.                                              |
| `renderToastItem`        | `(props) => Element`                         |                                                                               | Replace the whole toast. See <Doc href="/docs/in-app/customize-toasts#custom-ui">Custom UI</Doc>. |
| `renderToastItemContent` | `(props) => Element`                         |                                                                               | Replace the toast's content, keeping its frame.                                              |
| `onReady`                | `(ready: boolean) => void`                   |                                                                               | Called once the component can accept messages.                                               |
| `style`                  | `CSSProperties`                              | `position: fixed`, `width: 380px`, `top: 30px`, `right: 30px`, `z-index: 999` | Inline styles on the stack. Overriding one key keeps the rest.                               |

`dismissButton` defaults to `"auto"`, which means the button is always visible while
`autoDismiss` is off and appears on hover once it is on. `"hidden"` removes it, which
leaves a toast with no manual way out, so pair it with `autoDismiss`.

### What auto-dismiss actually does

**Only the top toast counts down.** The rest of the stack is held paused until it becomes
the top one, so three toasts at a 5 second timeout clear over 15 seconds rather than
together.

**Hovering anywhere on the stack pauses every countdown**, and leaving it resumes them, so
a toast a reader is part-way through does not vanish under the cursor. The countdown bar
pauses with it.

### Waiting for the component with onReady

A toast can arrive before the component has mounted, and a custom render function
registered too early is dropped. Both show up as toasts that never appear, or that appear
unstyled. Sign in from `onReady` when either applies.

```jsx theme={null}
const [toastReady, setToastReady] = useState(false);

useEffect(() => {
  if (toastReady) {
    courier.shared.signIn({ userId: "user_123", jwt });
  }
}, [toastReady]);

return <CourierToast onReady={setToastReady} renderToastItem={(props) => <CustomToast {...props} />} />;
```

## Verify

<Steps>
  <Step title="Open a screen with the toast component">
    Sign in a user. Keep the app open on a screen that renders the toast component (or your mobile listener).
  </Step>

  <Step title="Send a test message">
    <Doc href="/docs/in-app/send-to-the-inbox">Send a message</Doc> to that user on the `inbox` channel.
  </Step>

  <Step title="Confirm the toast">
    A toast pops up for the new message.
  </Step>
</Steps>
