Chat Source
Inline citation pills that credit an AI answer's sources, with a hover preview and a collapsible source list.
Description
ChatSource is a small pill that cites where a statement came from. It renders an <a> when it has a link and a <span> otherwise, wrapped in an inline <span>, so it drops straight into a sentence inside a <p>. URL sources label themselves with the domain and a favicon (or the domain's initial), document sources get a file icon, and any source with a link can open a hover card with the title and a short summary. ChatSources is the companion disclosure that collapses every source behind one trigger under the answer.
Use it in assistant messages: inline after the sentence a source backs up, as numbered footnote markers, or as the full list of pages and files a retrieval step pulled in. href is filtered to http:, https:, mailto: and relative URLs, so model output can be passed in without a separate sanitizing step.
Don't use it for files the user attached to their own prompt. That is ChatAttachment. For an ordinary link in body copy use Text Link, and for a rich hover card that is not tied to a citation use Popover.
ChatSource is a Create UI Pro component. Install it with a Pro seat.
Installation
Anatomy
Usage
Examples
Every example is a Pro preview (marked with a Pro badge).
Favicon
faviconUrl puts the site's icon in the pill. Without one, or when the image fails to load, the pill shows the first letter of the label instead. Pass an element as a child of ChatSource.Icon for a custom glyph.
Long titles
The pill truncates at max-w-32 and the full title stays available in the hover preview. className on ChatSource.Trigger lands on the pill itself, so max-w-72 there gives it more room.
Documents
sourceType="document" swaps the favicon for a file icon, and a source with no href is inferred as a document. Without a link the pill is static text. Give a document an href and it opens and previews like a web source.
Numbered citations
Pass the number as the child of ChatSource.Trigger for footnote-style markers, and render ChatSource.Preview yourself because children replace the default pair. A bare number is not a useful link name, so set aria-label.
Custom preview
Children of ChatSource.Preview replace the default card body and render inside PopoverBody, so they get the popover's padding. It forwards PopoverContent props, so size, side, align, sideOffset and showArrow work too. The preview only opens when the source has a link, since the link is its anchor.
Grouped sources
ChatSources collapses the full list under the answer. Stack a few ChatSource.Icons in the trigger; used outside a ChatSource, the icon reads faviconUrl from its own prop.
Controlled
Pass open and onOpenChange to drive the list from outside, for example from an "expand all" control. Use defaultOpen when you only need the starting state.
Accessibility
A linked pill is a regular <a> in the tab order. Focusing it opens the preview after openDelay, and moving focus away closes it. The card link repeats the pill's URL, so it is kept out of the tab order. ChatSources.Trigger is a <button> built on Radix Collapsible.
ARIA notes:
- The pill's accessible name is its visible text (the title, domain or file name). For a numbered citation, set
aria-labelonChatSource.Trigger, e.g.Source 1: Interaction to Next Paint. - Favicons render with
alt=""; the initial fallback, the document icon and the chevron arearia-hidden, so the label is announced once. - The preview is supplementary and does not move focus when it opens. Everything it shows should also be reachable through the link, because touch and screen reader users may never open it.
ChatSources.Triggergetsaria-expandedandaria-controlsfrom Radix. Put a count or "sources" in its text so the button has a name.- The pill is 20px tall with its hit area stretched to 24px.
- The preview animation, the collapse animation and the chevron rotation are disabled under
prefers-reduced-motion. - See the Radix Popover and Collapsible docs for the underlying behavior.
Styling
Tailwind override: className on ChatSource.Trigger merges onto the pill. className on the ChatSource root only reaches the inline wrapper around it.
Data slots and attributes: the component sets these for CSS targeting:
data-slot="chat-source"on the inline root wrapper, withdata-source-type("url"or"document").data-slot="chat-source-trigger"on the pill (<a>with a link,<span>without). It carriesdata-state("open"or"closed") while a preview is attached.data-slot="chat-source-icon"on the favicon<img>or a custom icon.data-slot="chat-source-icon-fallback"on the initial shown when there is no favicon or it fails to load.data-slot="chat-source-document-icon"on the file icon.data-slot="chat-source-title"on the truncating label.data-slot="chat-source-preview"on thePopoverContent, with Radixdata-stateanddata-side. The body inside keeps the popover's owndata-slot="popover-body".data-slot="chat-source-preview-link",-preview-header,-preview-titleand-preview-descriptionon the parts of the default card body.data-slot="chat-sources"on the collapsible root anddata-slot="chat-sources-trigger"on its button, both with Radixdata-state.data-slot="chat-sources-trigger-label"anddata-slot="chat-sources-indicator"on the trigger's label row and chevron.data-slot="chat-sources-content",data-slot="chat-sources-content-body"anddata-slot="chat-sources-list"on the collapsible panel, its padded body and the wrapping row of pills.
Related Components
- Chat Attachment: use it for files the user attached to their own message.
- Text Link: use it for an ordinary link in running text that is not a citation.
- Popover: use it for click-triggered cards with interactive content.
- File Format: use it when the file type needs a labeled badge rather than a citation.
API Reference
ChatSource
The root. Renders an inline <span> and extends React.ComponentProps<"span"> (minus title). With the default children it renders ChatSource.Trigger plus ChatSource.Preview, and wraps them in a Popover that opens on hover and focus when the preview is enabled.
Props
Variants
ChatSource has no variant axes. Restyle the pill through className on ChatSource.Trigger.
ChatSource.Trigger
The pill. Renders an <a> (new tab, rel="noopener noreferrer") when the source has a safe href, otherwise a <span>. Extends React.HTMLAttributes<HTMLElement> plus the anchor attributes below, and becomes the PopoverAnchor when the preview is enabled, so a click still follows the link.
Props
ChatSource.Icon
The favicon. Renders an <img> (loading="lazy", referrerPolicy="no-referrer"), the label's initial when there is no favicon or it fails, or the element you pass. Extends React.HTMLAttributes<HTMLElement>. Works outside a ChatSource when given faviconUrl.
Props
ChatSource.DocumentIcon
The file icon used by document sources. Extends the props of RiFileTextLine from @create-ui/assets/icons and is always aria-hidden.
Props
ChatSource.Title
The truncating label. Extends React.ComponentProps<"span">.
Props
ChatSource.Preview
The preview card. Renders PopoverContent (size="md", side="top" by default) with its content inside PopoverBody, and extends its props (minus title). Renders nothing unless the source has a safe href and the preview is enabled.
Props
ChatSources
The disclosure for a list of sources. Wraps Radix Collapsible.Root and extends its props.
Props
ChatSources.Trigger
The toggle button, with a chevron that turns when open. Wraps Radix Collapsible.Trigger and extends its props.
Props
ChatSources.Content
The animated panel. Wraps Radix Collapsible.Content and extends its props.
Props
ChatSources.List
A wrapping row of pills. Extends React.ComponentProps<"div">.
Props
Variant helpers
Every part's classes are exported as a CVA function with no variant axes, for building look-alike elements: chatSourceVariants, chatSourceTriggerVariants, chatSourceIconVariants, chatSourceIconFallbackVariants, chatSourceTitleVariants, chatSourcesVariants, chatSourcesTriggerVariants, chatSourcesContentVariants and chatSourcesListVariants.