Source cards
ProA row of cited sources under an AI answer where cards stream in with real site marks while the model searches, the overflow collapses into a stack of favicons, hovering lifts a preview with the passage, and an inline [n] citation highlights its card.
Why does my Stripe webhook fail signature verification in a Next.js route handler?
Sample sources. Simulated search, nothing was fetched.
- Citations under an AI search or docs assistant answer.
- Showing live search progress ("Searching 3 of 8") while an agent browses.
- Linking inline [n] references in generated text to the pages they came from.
- A full search results page with many results and filters; use a list layout instead.
- Tool calls with arguments and outputs; use tool-call.
- A single link preview inside prose; use a tooltip or hover card.
Installation
Pro source and install commands unlock with a Pro plan.
import { SourceCards, SourceCitation } from "@/components/arc/source-cards/source-cards"; <SourceCards sources={results} // [{ title, url, snippet, published }] status={searching ? "searching" : "done"} total={8} query="stripe webhook signature verification" duration={2100} onSourceClick={(source) => openReader(source.url)}> <p> Read the raw body with <code>await request.text()</code> before parsing it <SourceCitation n={1} /> <SourceCitation n={2} />. </p></SourceCards>API reference
4 parts. The first is the root.
SourceCards
A row of cited sources under an AI answer. Each card shows the site's mark, its domain, the citation number and a two line title. While searching, cards stream into reserved slots under "Searching 3 of 7"; past `max` the rest collapse into a stack of overlapping marks ("+4 more"). Hovering or focusing a card lifts a preview with the passage, and a SourceCitation in the answer highlights its card. With no `sources` it renders a self-playing demo.
sourcesSourceCardsSource[]–The sources in citation order: source 1 is cited as [1]. Append as results arrive; each new card fades into its slot. Leave out for the self-playing demo.status"searching" | "done" | "error""done"Searching shows a spinner, a shimmering "Searching", a rolling count and reserved slots. Done shows "7 sources" and the time. Error keeps what arrived and shows "Search failed" with Retry. Done with no sources shows "No sources found".totalnumber–How many sources the search will return. Drives "Searching 3 of 7" and reserves that many slots, so the row never shifts while it fills.querystring–The search query, shown muted after the status while searching or when nothing was found.durationnumber–Search time in ms, shown after the count when done ("7 sources · 2.1s").maxnumber3Cards shown before the rest collapse into the stack. The grid has max + 1 columns on wide containers and two below 540px.expandedboolean–Controls whether the stack is open. Pair with onExpandedChange.defaultExpandedbooleanfalseOpens the stack on first render.onExpandedChange(expanded: boolean) => void–Called from the stack, the Show all toggle, or a citation that points into the stack.highlightednumber | null–Controlled highlighted source number (1-based). Uncontrolled, a citation click highlights its card for 2.6s.onHighlightChange(n: number | null) => void–Called when a SourceCitation is clicked.onSourceClick(source: SourceCardsSource, n: number) => void–Makes cards buttons and calls this instead of navigating, for an in-app reader or analytics. Without it cards are links that open in a new tab.onRetry() => void–Shows Retry on error and Search again when nothing was found.errorstring–Detail shown after "Search failed", such as "The search provider timed out after 8s."hideStatusbooleanfalseHides the status line when your UI already shows search progress.childrenReactNode–The answer, rendered between the status line and the cards. Its height springs as it streams so the cards glide down. SourceCitation inside it is linked to the cards.labelstring"Sources"Accessible name of the source list.classNamestring–Class on the root element.SourceCitation
An inline [n] chip for the answer text, styled like the number on each card. Inside SourceCards it names its source for screen readers, turns accent while its card is highlighted, and highlights the card on click (opening the stack when needed).
nRequirednumber–The 1-based source number.onClick(event: MouseEvent<HTMLButtonElement>) => void–Extra click handler, for example to scroll the cards into view.classNamestring–Class on the chip.SourceCardsSource
One source.
titleRequiredstring–Page title as the search result shows it. Clamped to two lines on the card.urlRequiredstring–Full URL. The host becomes the domain and picks the mark: github.com, developer.mozilla.org, stripe.com, vercel.com, wikipedia.org and stackoverflow.com draw their real marks; anything else gets a lettered disc.idstring–Stable key. Defaults to the URL.sitestring–Readable site name for the preview and the stack, such as "Stripe Docs".snippetstring–The passage the answer relied on, shown in the hover preview (four lines).publishedstring–Short date label, such as "Oct 9, 2026".mark"github" | "mdn" | "stripe" | "vercel" | "wikipedia" | "stackoverflow"–Forces a built in mark when the URL does not match.iconReactNode–Custom favicon, drawn at 14px on the card and 16px in the preview.SourceCardsSimulation
The demo SourceCards renders without sources, exported for docs and previews.
scenario"live" | "error" | "empty""live"Live searches, streams a cited answer and tours two citations, looping until someone interacts. Error fails after two sources with Retry. Empty finds nothing and offers Search again.classNamestring–Class on the demo panel.- Tab
- Moves through citations, the Show all toggle, each card and the stack. A focused card shows its preview.
- EnterorSpace
- Opens a card (or calls onSourceClick), opens the stack, or highlights a citation's card.
- Escape
- Closes the open preview.
- The cards are a labelled list (aria-label "Sources") marked aria-busy while searching; skeleton slots are hidden from assistive technology.
- A polite live region announces status changes once: searching for the query, the number found, no sources, or the failure. Counts do not announce on every card.
- Each card's name reads number, title and domain, and says when it opens in a new tab. The preview is a tooltip linked with aria-describedby while open.
- SourceCitation is a real button named "Source 2: Request: text() method, developer.mozilla.org"; its highlighted state is never color alone because the card also gains an outline.
- Status always pairs an icon with words: spinner and Searching, globe and the count, alert and Search failed, crossed search and No sources found.
- The demo says in a live region that the search is simulated and that card clicks open nothing.
- Cards fade in with a 4px rise in 180ms as they arrive; skeletons crossfade out in 120ms. Nothing bounces.
- Collapsing the stack or a failed search dropping its reserved slots fades the leaving slots in place (120ms) while the stack or card taking their cell fades in, and the row height glides once. Skeleton bars sit on the same 18px lines as the text that replaces them.
- Counts roll 5px with tabular numerals in a reserved width; status icons and labels crossfade (100ms out, 160ms in).
- The answer's height springs (bounce 0) as it streams so the cards glide down instead of stepping one line at a time; a width change cuts straight to the new height.
- Previews open after a 320ms hover intent, then switch instantly between cards; they fade and rise 4px with a 0.98 scale in 160ms.
- Reduced motion keeps every end state and highlight but drops the rise, scale and skeleton pulse. The demo pauses off screen.
- Wide containers show max + 1 equal columns; below 540px (container query) the grid becomes two columns, with the stack as the last tile.
- Previews align to the card's left or right edge so they never leave the component, and drop below the card when there is no room above.
- On touch there is no hover preview; tapping a card opens it. Citation chips have an enlarged hit area.
- Cards are plain elements with CSS transitions; motion only drives arrivals, the preview and number rolls.
- One ResizeObserver per auto height box; the demo's timers stop off screen through an IntersectionObserver.
- Built in marks are inline SVG paths with no network requests.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use under an assistant message that cites web or docs sources. Keep sources in citation order so [n] maps to card n.
- Stream results by appending to sources with status "searching" and a total; switch to "done" when the model starts answering.
- Put the answer inside SourceCards as children when it contains SourceCitation chips; otherwise render SourceCards alone under your own answer and drive highlighted yourself.
- Pass onSourceClick for in-app reading; omit it for plain new-tab links. Do not wrap cards in your own links.
- Known hosts draw real marks automatically; pass icon for other favicons rather than inventing a logo.
The full library index for assistants is at /llms.txt.