Link Previews in Message Input
The purpose of link previews in the MessageInput
is to provide visual guides of what a user may expect to be rendered later in the MessageList
by Card
component among message attachments.
Rendering of link previews
The link previews are rendered using LinkPreviewList
. The component accepts a single prop linkPreviews
which is an array of LinkPreview
objects.
The default LinkPreviewList component
The default LinkPreviewList
component lists all the successfully loaded previews.
The default link preview UI is implemented for:
Message input
Edit message form
Enabling link previews
Link previews have to be enabled in two places:
- channel config property
url-enrichment
- enabled by default enrichURLForPreview
prop - disabled by default
Those who have not previously disabled url-enrichment
in the channel config, can enable link previews in MessageInput
by setting enrichURLForPreview
in one of the following places:
Channel props
import {
Channel,
ChannelHeader,
VirtualizedMessageList as MessageList,
MessageInput,
Thread,
Window,
} from 'stream-chat-react';
const App = () => (
<Channel enrichURLForPreview>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
);
export default App;
MessageList or VirtualizedMessageList (applied to EditMessageForm)
import { Channel, VirtualizedMessageList as MessageList } from 'stream-chat-react';
const App = () => (
<Channel>
{/* ... */}
<MessageList
additionalMessageInputProps={{
urlEnrichmentConfig: { enrichURLForPreview: true },
}}
/>
{/* ... */}
</Channel>
);
export default App;
Thread (applied to MessageInput)
import { Channel, Thread } from 'stream-chat-react';
const App = () => (
<Channel>
{/* ... */}
<Thread
additionalMessageInputProps={{
urlEnrichmentConfig: { enrichURLForPreview: true },
}}
/>
</Channel>
);
export default App;
MessageInput
import { Channel, MessageInput } from 'stream-chat-react';
const App = () => (
<Channel>
{/* ... */}
<MessageInput urlEnrichmentConfig={{ enrichURLForPreview: true }} />
{/* ... */}
</Channel>
);
export default App;
Link Preview customization
Custom rendering of link previews
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 component will be passed linkPreviews
, an array of LinkPreview
objects.
import { Channel, LinkPreviewListProps, LinkPreviewState } from 'stream-chat-react';
import { LinkPreviewCardLoaded, LinkPreviewCardLoading } from './LinkPreviewCard';
const CustomLinkPreviewList = ({ linkPreviews }: LinkPreviewListProps) => {
const showLinkPreviews = linkPreviews.length > 0;
if (!showLinkPreviews) return null;
return (
<div className='str-chat__link-preview-list'>
{Array.from(linkPreviews.values()).map((linkPreview) => {
switch (linkPreview.state) {
case LinkPreviewState.LOADED:
return (
<LinkPreviewCardLoaded key={linkPreview.og_scrape_url} linkPreview={linkPreview} />
);
case LinkPreviewState.LOADING:
return (
<LinkPreviewCardLoading key={linkPreview.og_scrape_url} linkPreview={linkPreview} />
);
case LinkPreviewState.QUEUED:
return (
<LinkPreviewCardLoading key={linkPreview.og_scrape_url} linkPreview={linkPreview} />
);
default:
return null;
}
})}
</div>
);
};
const App = () => (
<Channel LinkPreviewList={CustomLinkPreviewList} enrichURLForPreview>
{/* ... */}
</Channel>
);
export default App;
Link preview states
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 link is scheduled for enrichment. **/
QUEUED = 'queued',
}
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
In general, the behavior can be customized in two ways:
- globally through
Channel
props (enrichURLForPreviewConfig
) - with more granularity over
MessageList
,VirtualizedMessageList
,Thread
,MessageInput
props (additionalMessageInputProps.urlEnrichmentConfig
)
Custom debounce interval
The default debounce interval is 1.5 seconds. The URL discovery and enrichment will thus not start until the stops typing for at least 1.5 seconds. This interval can be increased or decreased by passing debounceURLEnrichmentMs
configuration parameter.
Global debounce interval configuration over Channel
:
import {
Channel,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
const debounceURLEnrichmentMs = 1000;
const App = () => (
<Channel enrichURLForPreview enrichURLForPreviewConfig={{ debounceURLEnrichmentMs }}>
<MessageList />
<MessageInput />
<Thread />
</Channel>
);
Local debounce configuration:
import {
Channel,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
const debounceURLEnrichmentMs = 1000;
const additionalMessageInputProps = {
urlEnrichmentConfig: { debounceURLEnrichmentMs },
};
const App = () => (
<Channel enrichURLForPreview>
<MessageList additionalMessageInputProps={additionalMessageInputProps} />
<MessageInput urlEnrichmentConfig={{ debounceURLEnrichmentMs }} />
<Thread additionalMessageInputProps={additionalMessageInputProps} />
</Channel>
);
Custom text parsing function
If the default link discovery functionality is not sufficient, this can be overridden by providing findURLFn
custom function. The requirement is that the function returns an array of strings - links - that will be later used to scrape the data.
The parameter set globally over Channel
props:
import {
Channel,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
import { searchForURLs } from '../utils';
const App = () => (
<Channel enrichURLForPreview enrichURLForPreviewConfig={{ findURLFn: searchForURLs }}>
<MessageList />
<MessageInput />
<Thread />
</Channel>
);
Local configuration of URL discovery function:
import {
Channel,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
import { searchForURLs } from '../utils';
const additionalMessageInputProps = {
urlEnrichmentConfig: { findURLFn: searchForURLs },
};
const App = () => (
<Channel enrichURLForPreview>
<MessageList additionalMessageInputProps={additionalMessageInputProps} />
<MessageInput urlEnrichmentConfig={{ findURLFn: searchForURLs }} />
<Thread additionalMessageInputProps={additionalMessageInputProps} />
</Channel>
);
Custom actions on link preview dismissal
When a link preview is dismissed, it's state is set to 'dismissed'
. This behavior can be expanded (not changed) by providing onLinkPreviewDismissed
callback. The callback is invoked at the beginning of the dismissal procedure. It is then followed by state update marking the given URL preview as 'dismissed'
.
The onLinkPreviewDismissed
callback can be passed to Channel
prop enrichURLForPreviewConfig
:
import {
Channel,
LinkPreview,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
const onLinkPreviewDismissed = (linkPreview: LinkPreview) => {
// custom logic to invoke, when a given link preview is dismissed
};
const App = () => (
<Channel enrichURLForPreview enrichURLForPreviewConfig={{ onLinkPreviewDismissed }}>
<MessageList />
<MessageInput />
<Thread />
</Channel>
);
The configuration can be passed individually to MessageList
, VirtualizedMessageList
, Thread
, MessageInput
:
import {
Channel,
LinkPreview,
MessageInput,
Thread,
VirtualizedMessageList as MessageList,
} from 'stream-chat-react';
const onLinkPreviewDismissed = (linkPreview: LinkPreview) => {
// custom logic to invoke, when a given link preview is dismissed
};
const additionalMessageInputProps = {
urlEnrichmentConfig: { onLinkPreviewDismissed },
};
const App = () => (
<Channel enrichURLForPreview>
<MessageList additionalMessageInputProps={additionalMessageInputProps} />
<MessageInput urlEnrichmentConfig={{ onLinkPreviewDismissed }} />
<Thread additionalMessageInputProps={additionalMessageInputProps} />
</Channel>
);
EnrichURLsController API
In case we would aspire at implementing custom MessageInput
components that would require control over link previews, we can access the API over the MessageInputContext
value. This is the API that allows us:
- to trigger URL search in message text and enrichment -
findAndEnqueueURLsToEnrich
- cancel the URL enrichment (for example when submitting a message) -
cancelURLEnrichment
- dismiss the loaded link previews assigning them with state
dismissed
-dismissLinkPreview
import { useMessageInputContext } from 'stream-chat-react';
const CustomMessageInputUI = () => {
const {
cancelURLEnrichment,
findAndEnqueueURLsToEnrich,
dismissLinkPreview,
} = useMessageInputContext();
// ...
};
const App = () => {
<Channel Input={CustomMessageInputUI} enrichURLForPreview>
{/* ... */}
</Channel>;
};
The findAndEnqueueURLsToEnrich
function serves as an indicator, whether the link preview feature is actually enabled in the application.