> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-llms-scoped-indexes.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" },
        "messagesRequestBuilder": { "type": "CometChat.MessagesRequestBuilder", "default": "undefined", "note": "Base request for the fetch. The component copies it and re-asserts setSavedOnly(true), setPinnedOnly(false) and clears any uid/guid; the limit prop wins over the builder's own setLimit()" },
        "textFormatters": { "type": "CometChatTextFormatter[]", "default": "undefined", "note": "Pass the same array given to CometChatMessageList so a row reads the same as it does in the conversation" }
      },
      "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>

<Note>
  **`messagesRequestBuilder` and `textFormatters` require `@cometchat/chat-uikit-react-native` 5.5.1 or later.** Neither prop exists in 5.5.0. Everything else on this page works on either version.
</Note>

## 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-docs-llms-scoped-indexes/AFh4cXjRRRN4kzr8/images/save.png?fit=max&auto=format&n=AFh4cXjRRRN4kzr8&q=85&s=3187cf7c8eda8cc1a93968901c932f8d" 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>

## Filtering

Pass a `messagesRequestBuilder` to control which saved messages are fetched — a search term, a
category or type filter, or the page size. Set `setSavedOnly(true)` on the builder to make the
intent explicit; the component re-asserts it on its own copy, so leaving it out is safe.

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

    <CometChatSavedMessages
      messagesRequestBuilder={new CometChat.MessagesRequestBuilder()
        .setSavedOnly(true) // explicit; the component re-asserts it anyway
        .setLimit(30)}
    />
    ```
  </Tab>
</Tabs>

<Note>
  The component works on a **copy** of your builder and re-asserts `setSavedOnly(true)` and
  `setPinnedOnly(false)` on it, so those are safe even if you omit them. Any `uid` / `guid` on the
  builder is cleared: saved messages are account-wide, and scoping them to one conversation would
  turn this cross-conversation inbox into a per-chat list. Your own instance is never mutated, so the
  same builder can also go to [`CometChatPinnedMessages`](/ui-kit/react-native/pinned-messages).
</Note>

The page size comes from the first of these that is set: the `limit` prop, then the builder's own
`setLimit()`, then `30`.

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

***

### messagesRequestBuilder

Base request used to fetch the saved messages — a search term, a category or type filter, or the
page size. See [Filtering](#filtering).

| | |
| - | - |
| Type | `CometChat.MessagesRequestBuilder` |
| Default | — |

The component copies the builder and re-asserts `setSavedOnly(true)` on the copy, so your instance is never
mutated. The `limit` prop wins over the builder's own `setLimit()`.

***

### textFormatters

Text formatters used when rendering the saved rows. Pass the same array you give
[`CometChatMessageList`](/ui-kit/react-native/message-list), otherwise a message reads differently
here than it does in the conversation. See
[Text Formatters](/ui-kit/react-native/custom-text-formatter-guide).

| | |
| - | - |
| Type | `CometChatTextFormatter[]` |
| Default | Default formatters from the data source |

***

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

`SavedMessageSource` is **not exported from the package root**, so annotate the parameter with its shape
rather than importing the name:

```ts theme={null}
type SavedMessageSource = {
  receiverType: string; // "user" | "group" — drives the sigil and the open-conversation call
  receiverId: string;   // the GUID, or the other party's UID in a 1:1
  label: string;        // display text, e.g. "#engineering" or "@alice"
  name: string;         // plain display name, no sigil — what the row title shows
  avatar?: string;      // group icon or user avatar, when the payload carries one
};
```

It is `null` when the payload has no hydrated conversation to resolve.

***

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.