> ## 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 a preference center

> Embed the Courier Preferences component so users pick their topics and channels.

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

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>;
};

**Try it live:**

<Card title="Preferences demo" icon="play" href="https://inbox-demo.courier.com/inbox-demo?layout=courier-preferences">
  See the preferences center in action.
</Card>

Courier Preferences is an embeddable UI where a signed-in user picks the topics they receive and how each is delivered. It uses the same SDKs and `signIn` as the inbox. If you already have the inbox, preferences is one more component.

<Frame caption="The Courier preferences center with its default styling.">
  <img src="https://mintcdn.com/courier-4f1f25dc/Ia7FGvhsFU_5CtOa/assets/preferences-default.webp?fit=max&auto=format&n=Ia7FGvhsFU_5CtOa&q=85&s=d31b2fb8c91c01bd97f26b09d0ecb4bc" alt="The Courier preferences center with its default styling: a topic section with per-topic toggles and an expanded channel picker for email, push, and SMS" className="mx-auto" width="3152" height="1776" data-path="assets/preferences-default.webp" />
</Frame>

<Warning>
  The component renders empty unless the user's JWT has `read:preferences write:preferences` in its scope string, as <Doc href="/docs/in-app/authenticate-users">Authenticate users</Doc> covers.
</Warning>

## 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-preferences
  ```

  ```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> and render the preferences component:

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

  export default function Preferences() {
    return <CourierPreferences />;
  }
  ```

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

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

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

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

  <template>
    <CourierPreferences />
  </template>
  ```

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

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

  ```swift iOS theme={null}
  // Use CourierPreferences (UIKit) or CourierPreferencesView (SwiftUI).
  // Both take a display mode; see the SDK reference for the options.
  import Courier_iOS

  // UIKit
  view.addSubview(CourierPreferences(mode: .channels(CourierUserPreferencesChannel.allCases)))

  // SwiftUI
  CourierPreferencesView(mode: .channels(CourierUserPreferencesChannel.allCases))
  ```

  ```kotlin Android theme={null}
  // Render the CourierPreferences view from com.courier.android.ui.preferences
  // XML: <com.courier.android.ui.preferences.CourierPreferences ... />
  val preferences = findViewById<CourierPreferences>(R.id.courierPreferences)
  ```

  ```dart Flutter theme={null}
  import 'package:courier_flutter/ui/preferences/courier_preferences.dart';

  // Render the preferences widget (see the SDK reference for mode options)
  CourierPreferences(mode: ChannelsMode(channels: CourierUserPreferencesChannel.allCases));
  ```

  ```jsx React Native theme={null}
  import { CourierPreferencesView } from "@trycourier/courier-react-native";

  export default function Preferences() {
    return <CourierPreferencesView />;
  }
  ```
</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>.

## Choose a display mode

The mobile components take a `mode` that sets how much control the user gets:

* **Topic mode**: one on/off toggle per subscription topic. The simplest option, and the usual choice.
* **Channels mode**: per-channel controls within each topic, so a user can keep a topic but receive it by email only.

<CodeGroup>
  ```swift iOS theme={null}
  CourierPreferencesView(mode: .topic)
  CourierPreferencesView(mode: .channels([.push, .sms, .email]))
  ```

  ```kotlin Android theme={null}
  // `mode` is a property on the view, not a constructor argument
  preferences.mode = CourierPreferences.Mode.Topic
  preferences.mode = CourierPreferences.Mode.Channels(
    listOf(CourierPreferenceChannel.PUSH, CourierPreferenceChannel.SMS, CourierPreferenceChannel.EMAIL)
  )
  ```

  ```dart Flutter theme={null}
  CourierPreferences(mode: TopicMode())
  CourierPreferences(mode: ChannelsMode(channels: [
    CourierUserPreferencesChannel.push,
    CourierUserPreferencesChannel.sms,
    CourierUserPreferencesChannel.email,
  ]))
  ```

  ```jsx React Native theme={null}
  <CourierPreferencesView mode={{ type: "topic" }} />
  <CourierPreferencesView mode={{ type: "channels", channels: ["push", "sms", "email"] }} />
  ```
</CodeGroup>

<Note>
  The web components do not take a display mode. They always render topics with their channel controls. Their `mode` prop is the **theme** mode: `"light"`, `"dark"`, or `"system"`. See <Doc href="/docs/in-app/customize-preferences#theme">Customize preferences</Doc>.
</Note>

You configure topics and sections in the <Guide href="/docs/guides/build-a-preference-center#configure-the-topics">preference center editor</Guide>. For how Courier enforces these choices at send time, see <Doc href="/docs/recipients/preferences/overview">how preferences resolve</Doc>.

## Props

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

| Prop            | Type                        | Description                                                                 |
| --------------- | --------------------------- | --------------------------------------------------------------------------- |
| `lightTheme`    | `CourierPreferencesTheme`   | Theme applied in light mode.                                                |
| `darkTheme`     | `CourierPreferencesTheme`   | Theme applied in dark mode.                                                 |
| `mode`          | `CourierComponentThemeMode` | Force light or dark instead of following the system.                        |
| `title`         | string                      | Heading above the topic list.                                               |
| `subtitle`      | string                      | Line under the heading.                                                     |
| `brandId`       | string                      | Render with a specific Brand's styling.                                     |
| `channelLabels` | `Record<string, string>`    | Rename a channel in the UI, `{ msteams: "Teams" }`.                         |
| `previewData`   | `CourierPreferencePage`     | Render injected data instead of fetching. No sign-in or network needed.     |
| `isLoading`     | boolean                     | Force the loading skeleton, for while your app fetches data it will inject. |
| `draft`         | boolean                     | Render the unpublished working draft instead of the published page.         |
| `onError`       | `(error: Error) => void`    | Called when the component fails to load or save.                            |
| `style`         | `CSSProperties`             | Inline styles on the wrapper.                                               |

The web component also exposes `setLightTheme`, `setDarkTheme`, `setMode`,
`setChannelLabels`, `setPreviewData`, and `setLoading` on the element, for hosts that hold a
ref rather than re-rendering with new props.

## Verify

<Steps>
  <Step title="Sign in with preference scopes">
    Sign in a user whose JWT includes `read:preferences write:preferences`.
  </Step>

  <Step title="Open the preferences screen">
    Open the screen that renders the preferences component.
  </Step>

  <Step title="Confirm topics persist">
    Your workspace's preference topics appear, and toggling one persists after a reload.
  </Step>
</Steps>
