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

# Saved Messages

> Display the logged-in user's saved messages across every conversation, with unsave and jump-to-message actions.

<Accordion title="AI Integration Quick Reference">
  ```json theme={null}
  {
    "component": "CometChatSavedMessages",
    "package": "@cometchat/chat-uikit-react-native",
    "import": "import { CometChatSavedMessages } from \"@cometchat/chat-uikit-react-native\";",
    "description": "Lists every message the logged-in user has saved, newest save first. A save is private and spans all conversations, so there is no user or group prop.",
    "requires": {
      "dashboardFlag": "Save Messages must be enabled for your app in the CometChat Dashboard",
      "resolution": "The kit reads the flag at login and re-reads it on every reconnection — no app code required"
    },
    "props": {
      "data": {
        "limit": { "type": "number", "default": 30, "note": "Page size for the fetch; the SDK caps it at 100" }
      },
      "callbacks": {
        "onBack": "() => void",
        "onItemPress": "(message: CometChat.BaseMessage, source: SavedMessageSource | null) => void"
      },
      "visibility": {
        "hideUnsaveMessageOption": { "type": "boolean", "default": false }
      },
      "customization": {
        "ItemView": "(message: CometChat.BaseMessage) => JSX.Element",
        "title": { "type": "string", "default": "localized" },
        "style": "DeepPartial<SavedMessagesStyle>"
      }
    }
  }
  ```
</Accordion>

`CometChatSavedMessages` lists every message the logged-in user has saved, newest save first. Unlike a
pin, a save is **private and spans conversations** — nobody else can see it, and the list is not scoped
to a single chat.

<Warning>
  **Save Messages must be enabled for your app in the CometChat Dashboard.** Until it is, the Save
  option never renders and this panel has nothing to show. The UI Kit reads that flag itself at login
  and on every reconnect — there is no app code to write.
</Warning>

## Where It Fits

Because the list is account-wide, this panel belongs at app level — a tab, a drawer entry, or a
profile screen — not inside a single conversation.

<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: a titled screen with a back arrow, listing the user's saved messages from across every conversation as conversation-style rows. Each row shows the source conversation's avatar and name, a one-line preview with a type glyph for media, documents, stickers and contacts, and the time it was saved." width="2880" height="1666" data-path="images/save.png" />
</Frame>

## Minimal Render

There is deliberately no `user` or `group` prop. Scoping it to one conversation would defeat the point.

<Tabs>
  <Tab title="TypeScript">
    ```tsx theme={null}
    import { CometChatSavedMessages } from "@cometchat/chat-uikit-react-native";

    <CometChatSavedMessages
      onBack={() => navigation.goBack()}
      onItemPress={(message: CometChat.BaseMessage, source) => {
        // `source` carries the conversation the message came from
        openConversation(message, source);
      }}
    />
    ```
  </Tab>

  <Tab title="JavaScript">
    ```jsx theme={null}
    import { CometChatSavedMessages } from "@cometchat/chat-uikit-react-native";

    <CometChatSavedMessages
      onBack={() => navigation.goBack()}
      onItemPress={(message, source) => {
        openConversation(message, source);
      }}
    />
    ```
  </Tab>
</Tabs>

## Actions and Events

### Callback Props

#### onItemPress

Fires when a saved row is tapped. Because the rows come from different conversations, it hands you a `source` alongside the message so you can open the right chat.

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

function SavedWithRouting() {
  const handleItemPress = (message: CometChat.BaseMessage, source) => {
    navigation.navigate("Messages", {
      receiverType: source?.receiverType, // "user" | "group"
      receiverId: source?.receiverId,
      messageId: String(message.getId()),
    });
  };

  return <CometChatSavedMessages onItemPress={handleItemPress} />;
}
```

`source` is `null` when the conversation could not be resolved. Otherwise it carries:

| Field          | Type      | Description                                                          |
| -------------- | --------- | -------------------------------------------------------------------- |
| `receiverType` | `string`  | `"user"` or `"group"` — drives the open-conversation call            |
| `receiverId`   | `string`  | The GUID, or the UID of the other party in a 1-1                     |
| `name`         | `string`  | Plain display name — what the row title shows                        |
| `label`        | `string`  | Display text with its sigil, e.g. `#engineering` or `@alice`         |
| `avatar`       | `string?` | Group icon or user avatar, when the payload carries a hydrated party |

You can also read the routing off the message itself:

```tsx lines theme={null}
<CometChatSavedMessages
  onItemPress={(message: CometChat.BaseMessage) => {
    navigateToConversation({
      receiverType: message.getReceiverType(), // "user" or "group"
      receiverId: message.getReceiverId(),
      conversationId: message.getConversationId(),
    });
  }}
/>
```

#### onBack

Fires when the back affordance in the header is pressed.

```tsx lines theme={null}
<CometChatSavedMessages onBack={() => navigation.goBack()} />
```

### Events

| Event                                 | Fires when                                 |
| ------------------------------------- | ------------------------------------------ |
| `ccMessageSaved` / `ccMessageUnsaved` | This device saved or unsaved.              |
| `onMessageSaved` / `onMessageUnsaved` | The same user saved on **another** device. |

<Note>
  The panel keeps itself current without a manual refresh, so a save made on a phone appears on that
  user's tablet on its own.
</Note>

<Note>
  A save is **per-viewer**. The same message reads saved for the user who saved it and unsaved for
  everybody else — it is never a property of the message globally.
</Note>

***

## Custom View Slots

| Slot       | Signature                                         | Replaces         |
| ---------- | ------------------------------------------------- | ---------------- |
| `ItemView` | `(message: CometChat.BaseMessage) => JSX.Element` | Entire saved row |

The empty, error and loading states are not view slots on this component. Restyle them through `style.emptyStateStyle` and `style.errorStateStyle` instead — see [Styling](#styling).

### ItemView

Replace the whole row. The default row is a conversation row — avatar, source conversation name, and a one-line preview — so a replacement usually wants the same three pieces.

```tsx lines theme={null}
import { CometChatSavedMessages, useTheme } from "@cometchat/chat-uikit-react-native";
import { CometChat } from "@cometchat/chat-sdk-react-native";
import { View, Text } from "react-native";

function CustomSavedRow() {
  const theme = useTheme();

  const getItemView = (message: CometChat.BaseMessage) => (
    <View style={{ paddingHorizontal: 16, paddingVertical: 12 }}>
      <Text style={{ ...theme.typography.body.medium, color: theme.color.textPrimary }}>
        {message.getReceiverId()}
      </Text>
      <Text
        numberOfLines={1}
        style={{ ...theme.typography.caption1.regular, color: theme.color.textSecondary }}>
        {(message as CometChat.TextMessage).getText?.()}
      </Text>
    </View>
  );

  return <CometChatSavedMessages ItemView={getItemView} />;
}
```

***

## Common Patterns

### A saved-messages tab

Because the list is account-wide, a tab or drawer entry is the natural home for it.

```tsx lines theme={null}
<Tab.Screen
  name="Saved"
  children={() => (
    <CometChatSavedMessages
      onItemPress={(message: CometChat.BaseMessage) => openConversation(message)}
    />
  )}
/>
```

### Read-only list

```tsx lines theme={null}
<CometChatSavedMessages hideUnsaveMessageOption />
```

### A custom title and close icon

```tsx lines theme={null}
<CometChatSavedMessages
  title="Bookmarks"
  style={{ closeButtonIcon: require("./assets/chevron-left.png") }}
/>
```

***

## Styling

The component uses the theme system from `CometChatThemeProvider`. Pass a `style` prop to customize the appearance. Every default value resolves from a semantic theme token, so retheming the kit restyles this panel without touching it.

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

function StyledSavedMessages() {
  return (
    <CometChatSavedMessages
      style={{
        containerStyle: {
          backgroundColor: "#FEEDE1",
        },
        titleStyle: {
          color: "#F76808",
        },
        previewIconStyle: {
          tintColor: "#F76808",
        },
      }}
    />
  );
}
```

<Note>
  `SavedMessagesStyle` deliberately has **no `itemStyle`**. The rows are conversation rows and take
  their chrome from `theme.conversationStyles.itemStyle` — the same tokens the conversation list
  uses — so the two surfaces cannot drift apart. Restyle the rows there and both update together.
</Note>

### Style Properties

| Property                    | Type                                 | Description                                                         |
| --------------------------- | ------------------------------------ | ------------------------------------------------------------------- |
| `containerStyle`            | `ViewStyle`                          | Root container                                                      |
| `headerContainerStyle`      | `ViewStyle`                          | Header row holding the back control and title                       |
| `titleStyle`                | `TextStyle`                          | Header title text                                                   |
| `closeButtonIconStyle`      | `ImageStyle`                         | Size and tint of the back control                                   |
| `closeButtonIcon`           | `ImageSourcePropType \| JSX.Element` | Replaces the back icon itself                                       |
| `previewIconStyle`          | `ImageStyle`                         | Type glyph in the row subtitle — image, video, audio, file, sticker |
| `previewIconContainerStyle` | `ViewStyle`                          | Wrapper around that glyph                                           |
| `menuIconStyle`             | `ImageStyle`                         | Glyph in the long-press menu (Unsave)                               |
| `emptyStateStyle`           | `object`                             | `containerStyle`, `titleStyle`, `subTitleStyle`, `iconStyle`        |
| `errorStateStyle`           | `object`                             | `containerStyle`, `titleStyle`, `subTitleStyle`, `iconStyle`        |

***

## Props

All props are optional. There is deliberately no `user` or `group` prop — see [Minimal Render](#minimal-render).

### limit

Page size for the fetch.

|         |          |
| ------- | -------- |
| Type    | `number` |
| Default | `30`     |

The server rejects a value above 100.

***

### onBack

Called when the back affordance is pressed.

|         |              |
| ------- | ------------ |
| Type    | `() => void` |
| Default | —            |

***

### onItemPress

Called when a row is pressed. `source` identifies which conversation the message belongs to.

|         |                                                                                |
| ------- | ------------------------------------------------------------------------------ |
| Type    | `(message: CometChat.BaseMessage, source: SavedMessageSource \| null) => void` |
| Default | —                                                                              |

The `source` fields are listed under [Callback Props](#onitempress).

***

### hideUnsaveMessageOption

Hides **Unsave** in the row's long-press menu.

|         |           |
| ------- | --------- |
| Type    | `boolean` |
| Default | `false`   |

***

### ItemView

Replaces the default row entirely.

|         |                                                   |
| ------- | ------------------------------------------------- |
| Type    | `(message: CometChat.BaseMessage) => JSX.Element` |
| Default | —                                                 |

***

### title

Panel title.

|         |                            |
| ------- | -------------------------- |
| Type    | `string`                   |
| Default | Localized "Saved Messages" |

***

### style

Per-instance style overrides, merged over the theme's `savedMessagesStyles`.

|         |                                   |
| ------- | --------------------------------- |
| Type    | `DeepPartial<SavedMessagesStyle>` |
| Default | —                                 |

See [Styling](#styling).

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Pinned Messages" icon="thumbtack" href="/ui-kit/react-native/pinned-messages">
    The conversation-wide, everyone-sees-it counterpart
  </Card>

  <Card title="Message List" icon="comments" href="/ui-kit/react-native/message-list">
    Where the Save option is raised
  </Card>

  <Card title="Save A Message (SDK)" icon="bookmark" href="/sdk/react-native/save-message">
    The SDK methods underneath this component
  </Card>

  <Card title="Events" icon="tower-broadcast" href="/ui-kit/react-native/events">
    Every UI Kit event, in one place
  </Card>
</CardGroup>
