> ## 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` from the Node SDK (`@trycourier/courier` v7 and later, where the client is the default import). 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.
> To translate a template, write one locale at a time with `PUT /notifications/{id}/locales/{localeId}`, which merges into the existing translations. `PUT /notifications/{id}/content` and `PUT /notifications/{id}/elements/{elementId}` overwrite translations along with the content, so send each element's `id` and `locales` back, and first remove every `checksum` and any `locales` key that starts with an underscore.
> 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.

# Courier Web Components SDK

> Inbox, toast, and preferences as custom elements for any web app, with or without a framework.

The Web Components SDK ships Courier's in-app components as standard custom elements, so they work in plain HTML or alongside any framework.

* `<courier-inbox>`: inbox for displaying and managing messages
* `<courier-inbox-popup-menu>`: popup menu version of the inbox
* `<courier-toast>`: toast notifications for time-sensitive alerts
* `<courier-preferences>`: preference center for topic subscriptions and delivery
* `CourierInboxDatastore` and `CourierToastDatastore`: the data layer, for custom UIs

<Tip>
  See these components in the [interactive Inbox demo](https://www.courier.com/inbox-demo). No setup required.
</Tip>

## Installation

The inbox, toast, and preferences are separate packages, so install only the ones you render.

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

Available on GitHub and npm:
<Link href="https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-ui-inbox"><Icon icon="github" iconType="solid" /> Inbox</Link> ·
<Link href="https://www.npmjs.com/package/@trycourier/courier-ui-inbox"><Icon icon="npm" iconType="solid" /> Inbox</Link> ·
<Link href="https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-ui-toast"><Icon icon="github" iconType="solid" /> Toast</Link> ·
<Link href="https://www.npmjs.com/package/@trycourier/courier-ui-toast"><Icon icon="npm" iconType="solid" /> Toast</Link> ·
<Link href="https://github.com/trycourier/courier-web/tree/main/%40trycourier/courier-ui-preferences"><Icon icon="github" iconType="solid" /> Preferences</Link> ·
<Link href="https://www.npmjs.com/package/@trycourier/courier-ui-preferences"><Icon icon="npm" iconType="solid" /> Preferences</Link>

The packages have no peer dependencies and need no build configuration.

<Note>
  Using React, Vue, or Angular? The [React](/docs/sdk-libraries/courier-react-web), [Vue](/docs/sdk-libraries/courier-vue-web), and [Angular](/docs/sdk-libraries/courier-angular-web) SDKs wrap these same elements as native components, with props and bindings in that framework's idiom.
</Note>

## Authentication

Courier authenticates with a **JWT** that your backend mints with your Courier API key, never in client code. [Authenticate users](/docs/in-app/authenticate-users?lang=Web%20Components) is the full guide: the token flow, scope strings, reading auth state, signing out, and token refresh. Using the EU datacenter? See [EU setup](/docs/in-app/authenticate-users?lang=Web%20Components#connect-to-the-eu-datacenter).

```ts theme={null}
import { Courier } from "@trycourier/courier-ui-inbox";

Courier.shared.signIn({ userId, jwt });  // also accepts tenantId, apiUrls, showLogs
Courier.shared.signOut();
Courier.shared.addAuthenticationListener(({ userId }) => { /* ... */ });
```

All three packages re-export the same `Courier` from `@trycourier/courier-js`. Import it from any one of them and sign in once, and every Courier element on the page uses that session and its real-time connection.

## Quick start

Importing a package registers its elements, so the tag renders as soon as the module loads. Sign the user in and the inbox fills.

```html theme={null}
<body>
  <courier-inbox id="inbox"></courier-inbox>

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

    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user
    Courier.shared.signIn({
      userId: "user_123",
      jwt: jwt,
    });
  </script>
</body>
```

<Tip>
  Follow the [inbox guide](/docs/in-app/add-an-inbox?lang=Web%20Components) for step-by-step guidance, including backend JWT generation. The [Web Components example app](https://github.com/trycourier/courier-web/tree/main/examples/web-js) wires up every element.
</Tip>

## Component reference

Each component's options, theme, callbacks, and custom-render surface live with the feature. These links open on the Web Components tab.

| To do this | Go here |
| - | - |
| Embed the inbox, or render it as a popup | [Add an inbox](/docs/in-app/add-an-inbox?lang=Web%20Components) |
| Theme it, handle clicks, set its actions | [Customize the inbox](/docs/in-app/customize-the-inbox?lang=Web%20Components) |
| Replace parts of it, or build your own | [Build a custom inbox](/docs/in-app/build-a-custom-inbox?lang=Web%20Components) |
| Split it into filtered tabs and feeds | [Tabs and feeds](/docs/in-app/tabs-and-feeds?lang=Web%20Components) |
| Add toasts, and set auto-dismiss | [Add toasts](/docs/in-app/add-toasts?lang=Web%20Components) |
| Theme a toast or replace it | [Customize toasts](/docs/in-app/customize-toasts?lang=Web%20Components) |
| Add a preference center | [Add a preference center](/docs/in-app/add-a-preference-center?lang=Web%20Components) |
| Theme preferences, or build your own | [Customize preferences](/docs/in-app/customize-preferences?lang=Web%20Components) |
| Send a message that reaches the inbox | [Send to the inbox](/docs/in-app/send-to-the-inbox) |

The `CourierInboxTheme`, `CourierToastTheme`, and `CourierPreferencesTheme` objects are the same on React, Vue, Angular, and the Web Components, so each is documented once: [inbox theme](/docs/in-app/customize-the-inbox#courierinboxtheme-reference), [toast theme](/docs/in-app/customize-toasts#theme), [preferences theme](/docs/in-app/customize-preferences#theme).

## Attributes and methods

Every option is an HTML attribute, a method on the element, or both. Attributes suit static setup written in markup. Methods suit anything that changes at runtime or takes a function, and they are what the framework SDKs call under the hood.

```html theme={null}
<!-- Attributes take strings, so a theme or feed list is a JSON string -->
<courier-inbox
  mode="light"
  light-theme='{"inbox": {"header": {"backgroundColor": "#FFFFFF"}}}'>
</courier-inbox>

<!-- Methods take objects and functions -->
<courier-inbox id="inbox"></courier-inbox>
<script type="module">
  const inbox = document.getElementById("inbox");

  inbox.setMode("light");
  inbox.setLightTheme({ inbox: { header: { backgroundColor: "#FFFFFF" } } });
</script>
```

| Element | Attributes |
| - | - |
| `<courier-inbox>` | `height`, `feeds`, `mode`, `light-theme`, `dark-theme`, `message-click`, `message-action-click`, `message-long-press` |
| `<courier-inbox-popup-menu>` | `popup-alignment`, `popup-width`, `popup-height`, `top`, `right`, `bottom`, `left`, `feeds`, `mode`, `light-theme`, `dark-theme` |
| `<courier-toast>` | `auto-dismiss`, `auto-dismiss-timeout-ms`, `dismiss-button`, `mode`, `light-theme`, `dark-theme` |
| `<courier-preferences>` | `title`, `subtitle`, `brand-id`, `draft`, `mode`, `light-theme`, `dark-theme` |

The inbox, the popup menu, and the preferences element also take `preview`, which renders injected data with no sign-in. Pair it with `setPreviewData()`.

Render slots such as `setListItem()`, `selectFeed()`, `refresh()`, and the toast's `onToastItemClick()` exist only as methods. Each one is documented on the In-App page for its feature.

**A theme attribute must be valid JSON.** A malformed `light-theme` or `dark-theme` isn't applied, and the inbox and toast throw a parse error to the console. Build the string with `JSON.stringify()`, or call `setLightTheme()` with the object instead.

**`auto-dismiss` turns off only when set to `"false"`.** Any other value, including an empty attribute, turns it on, and removing the attribute leaves it on. Call `disableAutoDismiss()` to turn it off from code.

## Events

Besides its `onMessage*` methods, `<courier-inbox>` dispatches a DOM event for each interaction. The `detail` carries the same payload the method's handler receives.

| Event | `detail` |
| - | - |
| `message-click` | `{ message, index }` |
| `message-action-click` | `{ message, action, index }` |
| `message-long-press` | `{ message, index }` |

```js theme={null}
document.addEventListener("message-click", (event) => {
  openInApp(event.detail.message);
});
```

The events bubble out of the element's shadow DOM, so you can listen on the element, on `<courier-inbox-popup-menu>`, or on `document`. That suits a host that delegates events from one place, or that loads the element before its own code runs.

The `message-click`, `message-action-click`, and `message-long-press` attributes take a function body as a string, the way `onclick` does, and run it with `props` in scope. The element compiles it with `new Function`, so a Content Security Policy without `'unsafe-eval'` blocks it. Use the event or the method under a strict CSP.

## Typing the elements

Each package exports its element class, so a TypeScript host can type the element it queries and get the methods autocompleted.

```ts theme={null}
import { CourierInbox } from "@trycourier/courier-ui-inbox";
import type { CourierToast } from "@trycourier/courier-ui-toast";
import type { CourierPreferences } from "@trycourier/courier-ui-preferences";

const inbox = document.getElementById("inbox") as CourierInbox;
inbox.setFeeds(CourierInbox.defaultFeeds());
```

## Advanced

### Server-side rendering

Importing a package on the server is safe. The elements register only when `window` exists, so the import is a no-op under Node, and the tags render once the module runs in the browser. In Next.js, import the package from a client component.

### Troubleshooting

<AccordionGroup>
  <Accordion title="The element renders nothing">
    The package isn't imported on that page, so the browser treats the tag as an unknown element. Import the package (`@trycourier/courier-ui-inbox` for the inbox) in a module that runs on that page.
  </Accordion>

  <Accordion title="A method call throws 'is not a function'">
    The script ran before the element was registered. Import the package first, or wait with `await customElements.whenDefined("courier-inbox")` before calling methods.
  </Accordion>

  <Accordion title="Messages not loading">
    **Possible causes:**

    1. Not authenticated: call `Courier.shared.signIn()`
    2. JWT expired: generate a new token
    3. Missing scopes: include `inbox:read:messages` and `inbox:write:events` in the JWT
    4. Wrong user ID: the `userId` must match the user the JWT was issued for
  </Accordion>
</AccordionGroup>
