Add citations and source cards to an AI answer

Keep citation references aligned with retrieved sources and give readers a direct way to inspect the evidence.

Live example

Keep the draft and response coordinated in a common parent. Announce important state changes without pulling focus away from the conversation.

Install the components

Add these to a React project with Tailwind CSS configured.

npx shadcn@latest add https://gradientdeck.com/ai-components/r/inline-citation-pill.json
npx shadcn@latest add https://gradientdeck.com/ai-components/r/sources-list-cards.json

Components used: Inline citation, Sources list.

1. Keep sources with the answer

Store the retrieved documents and the answer parts together. CitedAnswer uses one-based citation numbers: cite 1 refers to the first source. Preserve that order when a new chunk of the answer arrives. Sorting the source array independently can silently attach a claim to the wrong document.

2. Validate references at the boundary

Check that every citation is an integer within the source array. This example filters invalid references from a known local fixture. For model output, validate the complete payload on your server and flag missing evidence rather than presenting an unsupported claim as verified.

3. Provide both context and a destination

The citation pill shows a short source preview. SourceCards supplies direct links to the full documents. The current pill variant identifies sources by domain, so keep exact document URLs in the source cards. Only render URLs accepted by your application's URL validation policy.

Complete example

Save this as a client component after installing the components above.

"use client";

import { CitedAnswer, type AnswerPart } from "@/components/ai/inline-citation-pill";
import { SourceCards } from "@/components/ai/sources-list-cards";

// Keep citation numbers stable for the lifetime of this answer.
const sources = [
  { site: "React", domain: "react.dev", title: "Sharing state between components", snippet: "Move coordinated state to a common parent and pass values and handlers down.", color: "#357a9a", url: "https://react.dev/learn/sharing-state-between-components" },
  { site: "W3C", domain: "w3.org", title: "Understanding status messages", snippet: "Expose important status changes to assistive technology without moving focus.", color: "#6559aa", url: "https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html" },
];
const parts: AnswerPart[] = [
  "Keep the draft and response coordinated in a common parent.", { cite: 1 },
  " Announce important state changes without pulling focus away from the conversation.", { cite: 2 },
];

export default function CitedAnswersExample() {
  // Validate citation references at your data boundary before rendering model output.
  const validated = parts.filter(part => typeof part === "string" || (Number.isInteger(part.cite) && part.cite >= 1 && part.cite <= sources.length));
  return <div className="w-full max-w-xl space-y-5">
    <CitedAnswer parts={validated} sources={sources} />
    <SourceCards sources={sources} limit={2} />
  </div>;
}

Connect your backend

A citation is a navigation aid, not a fact check. Verify that retrieved passages support the adjacent claim, preserve provenance, and distinguish quoted material from a model's summary. This example uses two real documentation pages and a fixed answer; it does not perform retrieval.

All guides →