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]);
};Link Previews in Message Input
Link previews in MessageInput give users a visual hint of what will render later in the MessageList via the Card component.
Best Practices
- Enable link previews only when they add real value to your UX.
- Keep preview rendering lightweight and dismissible.
- Use
MessageComposerconfig for behavior changes instead of custom hacks. - Handle failed previews gracefully to avoid broken UI.
- Debounce URL detection to reduce unnecessary requests.
Link previews are rendered once enabled via MessageComposer:
Rendering link previews
Link previews are rendered via LinkPreviewList, which subscribes to MessageComposer.linkPreviewsManager state.
The default list shows previews that are loading or loaded.
The default link preview UI is implemented for:
Message input

Edit message form

Link Preview customization
Custom rendering of link previews
If the default UI isn’t enough, provide a custom LinkPreviewList via the Channel LinkPreviewList prop. Your component should subscribe to the 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>
);Link preview states
Each LinkPreview has a state property that you can use to render different UI. Possible states:
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
You can customize these aspects of link preview management:
- 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 preview dismissed",
origin: { emitter: composer.linkPreviewsManager },
});
},
},
});
});
}, [chatClient]);
};