This is beta documentation for Stream Chat React SDK v14. For the latest stable version, see the latest version (v13) .

Link Previews in Message Input

Link previews in MessageInput give users a visual hint of what will render later in the message list via the attachment card UI.

Best Practices

  • Enable link previews only when they improve the compose experience for your users.
  • Keep custom preview rendering lightweight and easy to dismiss.
  • Prefer MessageComposer configuration for behavior changes such as enablement, URL detection, and debounce timing.
  • Use WithComponents for shared UI overrides instead of patching Channel.
  • Test failed and loading states so the composer stays stable when enrichment is slow.

Link previews are controlled through MessageComposer configuration:

import { useEffect } from "react";
import { useCreateChatClient } from "stream-chat";

const App = () => {
  const chatClient = useCreateChatClient({
    apiKey,
    tokenOrProvider: userToken,
    userData: { id: userId },
  });

  useEffect(() => {
    if (!chatClient) return;

    chatClient.setMessageComposerSetupFunction(({ composer }) => {
      composer.updateConfig({
        linkPreviews: {
          enabled: true,
        },
      });
    });
  }, [chatClient]);

  return null;
};

The default MessageInputFlat renders LinkPreviewList inside the composer preview stack. By default it shows one loaded or loading preview at a time.

To replace the shared preview list UI, register a custom LinkPreviewList with WithComponents:

import { LinkPreviewsManager } from "stream-chat";
import {
  Channel,
  LinkPreviewCard,
  MessageInput,
  MessageList,
  WithComponents,
  useMessageComposer,
  useStateStore,
} from "stream-chat-react";

const selectLinkPreviews = (state) => ({
  linkPreviews: Array.from(state.previews.values()).filter(
    (preview) =>
      LinkPreviewsManager.previewIsLoaded(preview) ||
      LinkPreviewsManager.previewIsLoading(preview),
  ),
});

const CustomLinkPreviewList = () => {
  const { linkPreviewsManager } = useMessageComposer();
  const { linkPreviews } = useStateStore(
    linkPreviewsManager.state,
    selectLinkPreviews,
  );

  if (!linkPreviews.length) return null;

  return (
    <div className="custom-link-preview-list">
      {linkPreviews.map((linkPreview) => (
        <LinkPreviewCard
          key={linkPreview.og_scrape_url}
          linkPreview={linkPreview}
        />
      ))}
    </div>
  );
};

const App = () => (
  <WithComponents overrides={{ LinkPreviewList: CustomLinkPreviewList }}>
    <Channel>
      <MessageList />
      <MessageInput />
    </Channel>
  </WithComponents>
);

Change How Many Previews Are Visible

If you only need a different visible count, wrap the default LinkPreviewList and pass displayLinkCount:

import {
  Channel,
  LinkPreviewList,
  MessageInput,
  WithComponents,
} from "stream-chat-react";

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

const App = () => (
  <WithComponents overrides={{ LinkPreviewList: MultiPreviewLinkPreviewList }}>
    <Channel>
      <MessageInput />
    </Channel>
  </WithComponents>
);

Behavior Customization

You can customize URL discovery and enrichment behavior through MessageComposer configuration:

import { useEffect } from "react";
import type { LinkPreview } from "stream-chat";
import { useCreateChatClient } from "stream-chat";

import { customUrlDetector } from "./urlDetection";

const App = () => {
  const chatClient = useCreateChatClient({
    apiKey,
    tokenOrProvider: userToken,
    userData: { id: userId },
  });

  useEffect(() => {
    if (!chatClient) return;

    chatClient.setMessageComposerSetupFunction(({ composer }) => {
      composer.updateConfig({
        linkPreviews: {
          debounceURLEnrichmentMs: 2000,
          enabled: true,
          findURLFn: customUrlDetector,
          onLinkPreviewDismissed: (linkPreview: LinkPreview) => {
            chatClient.notifications.addInfo({
              message: `Dismissed ${linkPreview.og_scrape_url}`,
              origin: { emitter: composer.linkPreviewsManager },
            });
          },
        },
      });
    });
  }, [chatClient]);

  return null;
};