> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-feat-rn-pin-save-thread-subscription-uik.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Pin & Save Messages

> Let users pin important messages for everyone in a conversation and save messages privately for themselves.

## Goal

By the end of this guide you will have a chat screen where users can **pin** a message so it's highlighted for everyone in the conversation, open a screen of all pinned messages, and **save** a message privately to their own list — with a dedicated "Saved" screen to review saves across every conversation.

Pin and save are two separate concepts:

|                 | Pin                                          | Save                                         |
| --------------- | -------------------------------------------- | -------------------------------------------- |
| **Visible to**  | Everyone in the conversation                 | Only the current user                        |
| **Scope**       | One conversation                             | All conversations                            |
| **Surfaced by** | `CometChatPinnedMessages` (per conversation) | `CometChatSavedMessages` (a personal screen) |
| **Opened from** | The message header's pinned-messages button  | Your own navigation (no built-in trigger)    |

## Prerequisites

* Completed the [Integration Guide](/ui-kit/react-native/react-native-cli-integration)
* An existing chat screen using `CometChatMessageHeader`, `CometChatMessageList`, and `CometChatMessageComposer`
* **Pin messages** and **Save messages** enabled for your app through the `features.ux.messages.pinned.enabled` and `features.ux.messages.saved.enabled` app settings. See [Core Features → Pin & Save](/ui-kit/react-native/core-features#pin-and-save-messages).

<Note>
  The pin/unpin and save/unsave options only appear in the message options when the corresponding feature is enabled for your app. The UI Kit reads that setting at login and re-reads it on every reconnection, so no extra wiring is needed to show or hide the options.
</Note>

## Step 1: The Message Options

Once the features are enabled, `CometChatMessageList` automatically adds **Pin**, **Unpin**, **Save**, and **Unsave** to the message options — no props required. You only need the `hide*` props if you want to remove one:

*File: Messages.tsx*

```tsx theme={null}
import { CometChatMessageList } from "@cometchat/chat-uikit-react-native";

<CometChatMessageList
  user={user}
  group={group}
  // Options are shown by default; pass hide* props only to remove them:
  // hidePinMessageOption={true}
  // hideUnpinMessageOption={true}
  // hideSaveMessageOption={true}
  // hideUnsaveMessageOption={true}
/>
```

The **Pin/Unpin option is shown to every member** — the UI Kit does not gate it by role. Permission is enforced by the **server**: if a member isn't allowed to pin or unpin in that conversation, the action is rejected and the UI Kit shows a permission toast. Saving is per-user and always available.

<Note>
  The options are hidden on messages the server would refuse anyway — an unsent message with no ID yet, a deleted message, or one still awaiting a moderation verdict. A message sent seconds ago may briefly show no pin or save option while moderation is pending; it appears once the message is approved.
</Note>

## Step 2: Open the Pinned Messages Screen

`CometChatMessageHeader` can show a pinned-messages button. Set `showPinnedMessagesButton` and wire `onPinnedMessagesPress` to navigate to `CometChatPinnedMessages`, scoped to the same `user`/`group`.

*File: Messages.tsx*

```tsx theme={null}
import { CometChatMessageHeader } from "@cometchat/chat-uikit-react-native";

<CometChatMessageHeader
  user={user}
  group={group}
  showPinnedMessagesButton={true}
  onPinnedMessagesPress={() =>
    navigation.navigate("PinnedMessages", { user, group })
  }
/>
```

*File: PinnedMessages.tsx*

```tsx theme={null}
import { CometChat } from "@cometchat/chat-sdk-react-native";
import { CometChatPinnedMessages } from "@cometchat/chat-uikit-react-native";
import { useNavigation, useRoute } from "@react-navigation/native";
import { SafeAreaView } from "react-native";

const PinnedMessages = () => {
  const navigation = useNavigation<any>();
  const route = useRoute<any>();
  const { user, group } = route.params ?? {};

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <CometChatPinnedMessages
        user={user}
        group={group}
        onBack={() => navigation.goBack()}
        onItemPress={(message: CometChat.BaseMessage) => {
          // Jump the main message list to this message.
          navigation.navigate("Messages", {
            user,
            group,
            messageId: String(message.getId()),
          });
        }}
      />
    </SafeAreaView>
  );
};

export default PinnedMessages;
```

`CometChatPinnedMessages` is **per conversation** — it takes the same `user` or `group` the chat screen was opened with, and lists that conversation's pinned messages newest-pin first.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-feat-rn-pin-save-thread-subscription-uik/Ot3kMQMhbSdXXHRn/images/pin.png?fit=max&auto=format&n=Ot3kMQMhbSdXXHRn&q=85&s=a40f8fba9c8188f0ae6144a6ae9bd7a4" alt="The Pinned Messages screen listing one conversation's pinned messages as full bubbles, each with a pin glyph beside its timestamp." width="2880" height="1666" data-path="images/pin.png" />
</Frame>

| Prop          | Type                | Description                                                           |
| ------------- | ------------------- | --------------------------------------------------------------------- |
| `user`        | `CometChat.User`    | 1-1 conversation. Mutually exclusive with `group`.                    |
| `group`       | `CometChat.Group`   | Group conversation. Mutually exclusive with `user`.                   |
| `limit`       | `number`            | Page size. Defaults to 30; the server caps a conversation at 100.     |
| `onBack`      | `() => void`        | Closes the screen — rendered as the back control in the header.       |
| `onItemPress` | `(message) => void` | Tapping a row, so the host can jump its message list to that message. |

## Step 3: Add a "Saved" Screen

`CometChatSavedMessages` is **user-level**, not per conversation — it lists everything the logged-in user has saved, across every chat. There is no built-in entry point, so open it from your own navigation: a menu item, a profile screen, or a tab.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-feat-rn-pin-save-thread-subscription-uik/Ot3kMQMhbSdXXHRn/images/save.png?fit=max&auto=format&n=Ot3kMQMhbSdXXHRn&q=85&s=263a15d78e10370db4adc18fe2a37de8" alt="The Saved Messages screen listing saved messages from across every conversation, each row showing its source conversation and a one-line preview." width="2880" height="1666" data-path="images/save.png" />
</Frame>

*File: SavedMessages.tsx*

```tsx theme={null}
import { CometChat } from "@cometchat/chat-sdk-react-native";
import { CometChatSavedMessages } from "@cometchat/chat-uikit-react-native";
import { useNavigation } from "@react-navigation/native";
import { SafeAreaView } from "react-native";

const SavedMessages = () => {
  const navigation = useNavigation<any>();

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <CometChatSavedMessages
        onBack={() => navigation.goBack()}
        onItemPress={async (message, source) => {
          if (!source) return;
          // `source` names the conversation as an id + type, so resolve it to the
          // object the chat screen expects before navigating.
          const party =
            source.receiverType === "group"
              ? { group: await CometChat.getGroup(source.receiverId) }
              : { user: await CometChat.getUser(source.receiverId) };

          navigation.navigate("Messages", {
            ...party,
            messageId: String(message.getId()),
          });
        }}
      />
    </SafeAreaView>
  );
};

export default SavedMessages;
```

Each row shows which conversation the message came from, so a saved message is never orphaned from its context. `source` carries that conversation as `receiverType` (`"user"` or `"group"`) and `receiverId`, plus `name`, `label` and an optional `avatar` — see the [component reference](/ui-kit/react-native/saved-messages#onitempress).

| Prop          | Type                        | Description                                                      |
| ------------- | --------------------------- | ---------------------------------------------------------------- |
| `limit`       | `number`                    | Page size. Defaults to 30; the server caps the whole set at 100. |
| `onBack`      | `() => void`                | Closes the screen.                                               |
| `onItemPress` | `(message, source) => void` | Receives the message and its resolved source conversation.       |

## Step 4: Pinned & Saved Indicators

No wiring needed. `CometChatMessageList` renders a pin glyph and a filled bookmark in the message's meta row, beside the timestamp, and keeps them in sync in real time:

* **Pin** is conversation-wide, so every participant sees the indicator appear and disappear as the message is pinned or unpinned.
* **Save** is private, so the indicator appears only for the user who saved it — and syncs to that user's **other devices**.

## Step 5: Limits

Both features are capped by the server, per app:

| Limit           | Applies to                         |
| --------------- | ---------------------------------- |
| Pinned messages | Per conversation                   |
| Saved messages  | Per user, across all conversations |

When a cap is reached, the action is rejected and the UI Kit shows a toast carrying the **server-provided limit** — the number is never hard-coded in the kit, so raising the cap for your app changes the message automatically.

## Complete Example

*File: Messages.tsx*

```tsx theme={null}
import { CometChat } from "@cometchat/chat-sdk-react-native";
import {
  CometChatMessageComposer,
  CometChatMessageHeader,
  CometChatMessageList,
} from "@cometchat/chat-uikit-react-native";
import { useNavigation, useRoute } from "@react-navigation/native";
import { View } from "react-native";

const Messages = ({ user, group }: { user?: CometChat.User; group?: CometChat.Group }) => {
  const navigation = useNavigation<any>();
  const route = useRoute<any>();
  // Set when this screen was opened from the Pinned or Saved list.
  const goToMessageId = route.params?.messageId;

  return (
    <View style={{ flex: 1 }}>
      <CometChatMessageHeader
        user={user}
        group={group}
        showPinnedMessagesButton={true}
        onPinnedMessagesPress={() =>
          navigation.navigate("PinnedMessages", { user, group })
        }
      />

      <View style={{ flex: 1 }}>
        <CometChatMessageList user={user} group={group} goToMessageId={goToMessageId} />
      </View>

      <CometChatMessageComposer user={user} group={group} />
    </View>
  );
};

export default Messages;
```

`goToMessageId` is what makes the jump actually happen: `CometChatMessageList` fetches the page around that ID and scrolls to it, so tapping a pinned or saved row lands on the message in its original conversation.

## Next Steps

* [CometChatPinnedMessages](/ui-kit/react-native/pinned-messages) — the full component reference
* [CometChatSavedMessages](/ui-kit/react-native/saved-messages) — the full component reference
* [Pin Message (SDK)](/sdk/react-native/pin-message) — the underlying SDK methods and listeners
* [Save Message (SDK)](/sdk/react-native/save-message)
* [Pin Conversation](/ui-kit/react-native/core-features#pin-conversations) — pinning a whole chat to the top of the list
