# Link previews

A link preview is the card with a title, a description and an image that shows for a link in a message. This page covers how the composer loads a preview while the user types, what's sent with the message, and how the message list shows it.

## What users get

The user types "Look at `https://example.com/launch`". When they stop typing, a card appears above the input with the page's image, title and description, and the URL. Hovering the card shows the full URL. They can dismiss it with its ✕. When they send, the message in the list shows a card for the link above the text.

## How it works across the layers

- **State.** The composer's `linkPreviewsManager` finds the links in the text, loads a preview for each, and keeps them in its store. Sending adds the loaded previews to the message as attachments.
- **UI.** `LinkPreviewList` shows the previews above the input, as `LinkPreviewCard`s. In the message list, `Attachment` renders each preview with `Card`.
- **Layout.** Not involved.

### From typing to sending

1. When the user stops typing, `messageComposer.linkPreviewsManager` finds the links in the text and loads a preview for each from the server.
2. Dismissing a preview with its ✕ keeps the link in the text, but the message goes out without that preview.
3. On send, the loaded previews go out as the message's attachments. If one is still loading, none go out, and the server may add its own.

[Loading previews](https://getstream.io/chat/docs/sdk/react/state/writing-messages/link-previews/#loading-previews) in State › Link previews covers the wait, the statuses and the manager's calls, and [Sending previews](https://getstream.io/chat/docs/sdk/react/state/writing-messages/link-previews/#sending-previews) what goes out with the message.

### In the composer and the message list

`LinkPreviewList` shows the previews that are loading or loaded. It shows only the first one by default; the others are still sent. `LinkPreviewCard` shows the image, title, description and URL, and its ✕ dismisses the preview.

In the message list, an attachment with `og_scrape_url` or `title_link` is a link preview, and `Attachment` renders it with `Card`.

## Turning it on or off

Link previews are on by default. Two sides decide:

- **The client:** `messageComposer.linkPreviews.enabled`, `true` by default. It decides whether the composer loads and shows previews.
- **The server:** the channel type's `url_enrichment` setting. It decides whether the server adds previews to messages. With it off, the composer doesn't load previews either.

To stop the composer from loading previews:

```ts
client.config.set({
  messageComposer: { linkPreviews: { enabled: false } },
});
```

The server still adds previews to sent messages while `url_enrichment` is on, so messages keep their cards; only the composer stops showing them. To send messages without cards, turn `url_enrichment` off for the channel type. [Configuration](https://getstream.io/chat/docs/sdk/react/state/state-and-entities/configuration/#who-decides-what) lists every setting both sides decide.

## Customizing

### Change how links are found and loaded

```ts
import type { LinkPreview } from "stream-chat";

client.config.set({
  messageComposer: {
    linkPreviews: {
      // Only links to your own domain get a preview.
      findURLFn: (text: string) =>
        Array.from(
          text.matchAll(/https:\/\/example\.com\/\S*/g),
          ([url]) => url,
        ),
      onLinkPreviewDismissed: (linkPreview: LinkPreview) => {
        console.log(`Dismissed ${linkPreview.og_scrape_url}`);
      },
    },
  },
});
```

`debounceURLEnrichmentMs` is read only when a composer is built, so set it before the client creates composers, for example in the `config` you pass to `useCreateChatClient`.

### Show more than one preview

Wrap `LinkPreviewList` and pass `displayLinkCount`:

```tsx
import {
  Channel,
  LinkPreviewList,
  MessageComposer,
  MessageList,
  WithComponents,
} from "stream-chat-react";

const ThreeLinkPreviews = () => <LinkPreviewList displayLinkCount={3} />;

const overrides = { LinkPreviewList: ThreeLinkPreviews };

export const ChannelWithThreePreviews = () => (
  <WithComponents overrides={overrides}>
    <Channel channel={channel}>
      <MessageList />
      <MessageComposer />
    </Channel>
  </WithComponents>
);
```

### Replace the preview list

A list of your own reads `linkPreviewsManager` from the composer and renders the previews it wants. `LinkPreviewsManager.previewIsLoaded()` and `LinkPreviewsManager.previewIsLoading()` tell the statuses apart:

```tsx
import type { LinkPreviewsManagerState } from "stream-chat";
import { LinkPreviewsManager } from "stream-chat";
import {
  LinkPreviewCard,
  useMessageComposerController,
  useStateStore,
} from "stream-chat-react";

const loadedPreviewsSelector = ({ previews }: LinkPreviewsManagerState) => ({
  previews: Array.from(previews.values()).filter(
    LinkPreviewsManager.previewIsLoaded,
  ),
});

export const LoadedLinkPreviews = () => {
  const { linkPreviewsManager } = useMessageComposerController();
  const { previews } = useStateStore(
    linkPreviewsManager.state,
    loadedPreviewsSelector,
  );

  if (!previews.length) return null;

  return (
    <div className="loaded-link-previews">
      {previews.map((preview) => (
        <LinkPreviewCard key={preview.og_scrape_url} linkPreview={preview} />
      ))}
    </div>
  );
};
```

Register it with `<WithComponents overrides={{ LinkPreviewList: LoadedLinkPreviews }}>`. It works in channel, thread and edit composers alike, because `useMessageComposerController()` returns the composer it's rendered in.

### Change the card in the message list

Pass a `Card` of your own to `Attachment`, and register the result as the `Attachment` override:

```tsx
import type { AttachmentProps, CardProps } from "stream-chat-react";
import { Attachment } from "stream-chat-react";

const CompactCard = ({ og_scrape_url, title, title_link }: CardProps) => (
  <a className="compact-card" href={title_link ?? og_scrape_url}>
    {title ?? og_scrape_url}
  </a>
);

export const AttachmentWithCompactCards = (props: AttachmentProps) => (
  <Attachment {...props} Card={CompactCard} />
);
```

## Reference

- [Attachments](https://getstream.io/chat/docs/sdk/react/component-reference/message/attachments/): the `Attachment` props, including `Card`.
- [Composer UI components](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/composer-ui-components/): `LinkPreviewList` and the composer's other parts.
- [MessageComposer API](https://getstream.io/chat/docs/sdk/react/reference/state/messagecomposer-api/): `linkPreviewsManager`.
- [Configuration tree](https://getstream.io/chat/docs/sdk/react/reference/state/configuration-tree/): every `messageComposer.linkPreviews` setting.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/features/rich-content/link-previews/](https://getstream.io/chat/docs/sdk/react/features/rich-content/link-previews/).