Citation
The mark an answer leaves where a claim came from: a chip in the running text that previews its sources on hover or focus, and goes to them on click. One chip per claim, however many sources back it.
Installation#
Usage#
import { Citation } from "@/components/ui/citation"
const source = {
href: "https://web.dev/articles/lcp",
title: "Largest Contentful Paint (LCP)",
description: "Sites should strive to have LCP of 2.5 seconds or less.",
}
export function Answer() {
return (
<p>
A good LCP is 2.5 seconds or less
<Citation index={1} sources={[source]} />.
</p>
)
}Anatomy#
Most of the time <Citation sources={…} /> is all you write: it renders the chip and the card from the data. Compose the parts when the chip needs its own label or the card its own content.
import {
Citation,
CitationTrigger,
CitationContent,
CitationSource,
} from "@/components/ui/citation"
<Citation sources={sources}>
<CitationTrigger />
<CitationContent>
<CitationSource />
</CitationContent>
</Citation>The card#
Each source is a row: the favicon and domain as a quiet eyebrow, the page title, then the passage the claim rests on, clamped to three lines. Several sources stack as several rows in one card, each a link of its own, with its hover fill set 8px into the card on the Popover's inset rung so the corners stay concentric.
Source chips#
variant="source" trades the counter for the favicon and the domain, with +N when more than one source backs the claim: the answer-engine register, where saying where without a hover is worth the width in the line. The counter stays the default because it reads quietest in dense prose.
In a chat#
Drop the chips straight into a Chat turn. The chip carries a data-slot, so the turn's prose styles leave it alone: markdown links get the underline, the chip keeps its own look.
API reference#
Citation
The root: the sources and the HoverCard state every part reads. With no children it renders the chip and the card from sources. Forwards Radix HoverCard.Root props.
| Prop | Type | Default | Description |
|---|---|---|---|
| sources* | CitationSourceData[] | - | { href, title?, description?, favicon? } per source. The chip links to the first. |
| index | number | - | The footnote number the number chip shows. Omitted, it shows the source count. |
| variant | "number" | "source" | "number" | number is a footnote counter; source shows the favicon, the domain and +N. |
| openDelay | number | 300 | Hover intent before the card opens, in ms. Shorter than a plain HoverCard's 700. |
| closeDelay | number | 200 | Delay before the card closes once the pointer leaves, in ms. |
| children | React.ReactNode | - | Compose the chip and card yourself with CitationTrigger and CitationContent. |
CitationTrigger
The chip: a Badge on a real link to the first source, opened in a new tab. Its accessible name lists every source, because the card is never keyboard-reachable. Forwards native <a> props except href.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Overrides the label (the index, or the domain and count). |
CitationContent
The preview card. With no children it lists every source; pass CitationSources to compose it. Forwards HoverCardContent props (side, align, density).
CitationSource
One source in the card: favicon and domain, the title, the extract. A link. Forwards native <a> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| href* | string | - | The page. The domain and favicon derive from it. |
| title | string | - | The page title, clamped to two lines. |
| description | string | - | The passage the claim rests on, clamped to three lines. |
| favicon | string | - | Overrides the favicon derived from the domain. |