Link previews

When the user types a text that resembles a URL link, the composer can show a preview of the page it points to: its title, description and image. The link previews manager, messageComposer.linkPreviewsManager, finds the links in the text, loads a preview for each one, and adds the loaded previews to the message attachments when it's sent.

Info:

Example

The user types "Look at https://example.com" and stops typing:

  1. After a short pause, the manager finds the link and adds a preview for it with the status loading.
  2. When the page's preview data arrives, the preview's status becomes loaded, and it holds the page's title, description and image.
  3. The user sends the message. The preview goes out as one of the message's attachments.

What it holds

linkPreviewsManager.state has one field, previews: a Map with one entry for each link found in the text. The key is the link, and the value is its preview. linkPreviewsManager.previews reads the current Map.

A preview is the attachment the message will carry for the link: the page's data, as the server's link enrichment returns it, plus a status. A loaded preview looks like this:

const preview = {
  og_scrape_url: "https://getstream.io/chat/",
  title: "Chat API & SDK",
  text: "Build real-time chat in less time.",
  title_link: "https://getstream.io/chat/",
  image_url: "https://getstream.io/images/og/chat.png",
  status: "loaded",
};

The status says where the preview is, and whether it goes out with the message:

status What it means Sent Getter
loading The page's data is being loaded. no linkPreviewsManager.loadingPreviews
loaded The data arrived. yes linkPreviewsManager.loadedPreviews
failed The request failed. No notification is published. no linkPreviewsManager.failedPreviews
dismissed The user removed it. It isn't loaded again while the link stays in the text. no linkPreviewsManager.dismissedPreviews
pending Added from code without a status. The manager doesn't load it. no linkPreviewsManager.pendingPreviews

Each getter returns the previews with that status, as an array.

const { linkPreviewsManager } = channel.messageComposer;

const unsubscribe = linkPreviewsManager.state.subscribeWithSelector(
  ({ previews }) => ({ previews }),
  ({ previews }) => {
    previews.forEach((preview, url) => {
      console.log(url, preview.status, preview.title);
    });
  },
);

Loading previews

While the user types, the manager watches the text. When typing pauses for the time the debounceURLEnrichmentMs setting gives, 1.5 seconds by default, it finds the links with the findURLFn setting and loads a preview for each link that doesn't have one yet.

When the text has no links left, every preview goes, the dismissed ones too.

What a preview list needs, and the call for each:

To… Call What happens
remove a preview the user doesn't want LinkPreviewsManager.dismissPreview(url) Marks the preview dismissed: the link stays in the text, but the message goes out without its preview, and the preview isn't loaded again while the link stays. Calls the onLinkPreviewDismissed setting with it.
stop previews from starting to load LinkPreviewsManager.cancelURLEnrichment() Drops the link search that the last change to the text scheduled, so no new preview starts loading until the user types again. Previews that are already loading still finish.
remove every preview LinkPreviewsManager.clearPreviews() Removes all previews except the dismissed ones, so those aren't loaded again.
add a preview from code, or change one LinkPreviewsManager.updatePreview(url, preview) Changes the fields you pass in the preview for url, and keeps the others. If url has no preview yet, adds one. Pass a status to set it; otherwise an existing preview keeps its status, and a new one is pending, which the manager doesn't load and the message doesn't send.

Sending previews

When the message is sent, its link previews come from one of two places.

  • From the composer. The loaded previews go out with the message, after its other attachments, and the server leaves those links alone. Previews with any other status don't go out.
  • From the server. If any preview is still loading when the message is sent, the composer sends none of them, not even the loaded ones. The server may then add its own, as the channel type's url_enrichment setting decides.

A draft keeps the loaded previews. When a draft or a message to edit is loaded into the composer, its previews come back as loaded.

When previews are on

Previews are on while linkPreviewsManager.enabled is true. That's when both the linkPreviews.enabled setting in client.config and the channel type's url_enrichment setting are on. Otherwise, the manager loads no previews.

Settings

Set them for every composer in client.config, under messageComposer.linkPreviews:

client.config.set({
  messageComposer: {
    linkPreviews: { debounceURLEnrichmentMs: 800 },
  },
});

To change a setting for one composer only, set it on that composer's link previews manager, for example channel.messageComposer.linkPreviewsManager.enabled = false.

Setting Default What it does
enabled true false turns off link previews. Combined with the channel type's url_enrichment setting.
debounceURLEnrichmentMs 1500 How long, in milliseconds, the user must pause typing before previews load. A composer reads it once, when it's created, as Settings read once explains.
findURLFn finds web links Returns the links in the text to load previews for. The default finds web addresses whose host name has a dot and a top-level domain of two or more letters, and adds https:// to a link typed without it.
onLinkPreviewDismissed none Called with the preview when the user dismisses it.

See Link previews for the feature.

Used in