Link Previews in Message Input

The purpose of link previews in the MessageInputis to provide visual guides of what a user may expect to be rendered later in theMessageListby [Card` component](/chat/docs/sdk/react/components/message-components/attachment#card/) among message attachments.

Link previews are rendered once enabled via MessageComposer:

import { useCreateChatClient } from " stream-chat";

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

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

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

The link previews are rendered using LinkPreviewList. The component subscribes to changes in MessageComposer.linkPreviewsManager state to render the list of link previews.

The default LinkPreviewList component lists all the successfully loaded and loading previews.

The default link preview UI is implemented for:

Message input

Link Preview Message Input


Edit message form

Link Preview Edit Message Form

If the default link previews UI does not meet our expectations, we can provide a custom component. To render our own LinkPreviewList, we just need to pass it to Channel prop `LinkPreviewList. The custom component should internally subscribe to changes in message composer’s link preview manager.

import {
  Channel,
  LinkPreviewListProps,
  LinkPreviewCard,
  useMessageComposer,
} from "stream-chat-react";
import { LinkPreviewsManagerState } from "stream-chat";

// on every link preview manager state extract these link previews
const linkPreviewsManagerStateSelector = (state: LinkPreviewsManagerState) => ({
  linkPreviews: Array.from(state.previews.values()).filter(
    (preview) =>
      LinkPreviewsManager.previewIsLoaded(preview) ||
      LinkPreviewsManager.previewIsLoading(preview),
  ),
});

const CustomLinkPreviewList = ({ linkPreviews }: LinkPreviewListProps) => {
  const { linkPreviewsManager } = useMessageComposer();

  // subscribe to link preview manager's state change
  const { linkPreviews } = useStateStore(
    linkPreviewsManager.state,
    linkPreviewsManagerStateSelector,
  );

  const showLinkPreviews = linkPreviews.length > 0;

  if (!showLinkPreviews) return null;

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

const App = () => (
  <Channel LinkPreviewList={CustomLinkPreviewList}>{/* ...  */}</Channel>
);

In the above example we can notice, that the LinkPreview object comes with property state. This property can be used to determine, how the preview for a given link should be rendered. These are the possible states a link preview can acquire:

enum LinkPreviewState {
  /** Link preview has been dismissed using MessageInputContextValue.dismissLinkPreview **/
  DISMISSED = "dismissed",
  /** Link preview could not be loaded, the enrichment request has failed. **/
  FAILED = "failed",
  /** Link preview has been successfully loaded. **/
  LOADED = "loaded",
  /** The enrichment query is in progress for a given link. **/
  LOADING = "loading",
  /** The preview reference enrichment has not begun. Default status if not set. */
  PENDING = "pending",
}

Behavior customization

The following aspect of link preview management in MessageInput can be customized:

  • The debounce interval for the URL discovery and enrichment requests.
  • URL discovery
  • Link preview dismissal

All is done via MessageComposer configuration API:

import { useCreateChatClient } from "stream-chat";
import type { LinkPreview } 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: "Link prevew dismissed",
              origin: { emitter: composer.linkPreviewsManager },
            });
          },
        },
      });
    });
  }, [chatClient]);
};
© Getstream.io, Inc. All Rights Reserved.