Sources
AICitations for generated answers: numbered inline chips with a preview card, and a collapsible list of every source used.
npx prodular-ui add sourcesInline citations
Numbered chips after the claim they support. Hover or focus one to preview the source.
import { Citation, type SourceData } from "@/components/ui/sources"
const sources: SourceData[] = [ { title: "ASC 606 revenue recognition: principal versus agent", url: "https://fasb.org/page/asc606-principal-agent", siteName: "FASB", snippet: "An entity is a principal if it controls the specified good or service before that good or service is transferred to a customer.", }, { title: "Marketplace fee policy, rev. 4", url: "https://www.notion.so/ledgerline/marketplace-fees", siteName: "Notion", snippet: "Ledgerline does not take title to partner services and does not set the price partners charge end customers.", },]
export default function SourcesCitations() { return ( <p className="max-w-xl text-[15px] leading-[1.65]"> Marketplace fees should be booked net. Ledgerline acts as an agent because it never controls the partner service before it reaches the customer <Citation index={1} source={sources[0]} />, and the fee policy confirms partners set their own prices <Citation index={2} source={sources[1]} />. Hover a number to preview the source. </p> )}Sources list
A row of favicons and a count that expands into the full list, in citation order.
import { Source, Sources, SourcesContent, SourcesTrigger, type SourceData,} from "@/components/ui/sources"
const sources: SourceData[] = [ { title: "Savings Plan SP-2291 expiration notice", url: "https://console.aws.amazon.com/billing/home", siteName: "AWS Billing" }, { title: "Q3 backfill: scale ingest workers to 48 nodes", url: "https://linear.app/ledgerline/issue/DATA-1184", siteName: "Linear" }, { title: "Drop expiration on events-raw while retention is reviewed", url: "https://github.com/ledgerline/infra/pull/2207", siteName: "GitHub" }, { title: "Cost and usage report, September 2026", url: "https://console.aws.amazon.com/cost-management/home", siteName: "Cost Explorer" }, { title: "BigQuery billing export, September 2026", url: "https://console.cloud.google.com/billing/reports", siteName: "Google Cloud" }, { title: "FinOps monthly review, August", url: "https://www.notion.so/ledgerline/finops-august", siteName: "Notion" },]
export default function SourcesList() { return ( <div className="flex w-full max-w-xl flex-col gap-8"> <Sources> <SourcesTrigger sources={sources} count={12} /> <SourcesContent> {sources.map((source, i) => ( <Source key={source.url} source={source} index={i + 1} /> ))} </SourcesContent> </Sources> <Sources defaultOpen> <SourcesTrigger sources={sources.slice(0, 3)} /> <SourcesContent> {sources.slice(0, 3).map((source, i) => ( <Source key={source.url} source={source} index={i + 1} /> ))} </SourcesContent> </Sources> </div> )}Anatomy
<Sources> <SourcesTrigger sources={sources} /> <SourcesContent> {sources.map((s, i) => <Source key={s.url} source={s} index={i + 1} />)} </SourcesContent></Sources>Guidance
Use it when
- Whenever an answer relies on retrieved documents, records or web pages.
- Inline citations right after the sentence they support, and the Sources list once under the answer.
Avoid
- Citing the model itself or sources it did not read.
- More than three citations on one sentence. Cite the strongest one.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Number citations from 1 in the order they first appear, and list Sources in the same order so numbers match.
- Only cite sources that the tool calls actually returned. Never generate URLs, titles or snippets.
- Inside Response, pass sources to Response and keep [1] markers in the text instead of rendering Citation yourself.
- Give each source a title and url. Add siteName for internal tools (Cost Explorer, Notion) and snippet with the exact supporting passage.
- Render the Sources list after the answer finishes streaming, not while it streams.
API
Citation
| Prop | Type | Default | Description |
|---|---|---|---|
| index | number | One based number shown in the chip. | |
| source | SourceData | { title, url, snippet?, favicon?, siteName? } |
SourcesTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| sources | SourceData[] | The first four favicons are stacked in the trigger. | |
| count | number | sources.length | Total shown in the label. |
Source
| Prop | Type | Default | Description |
|---|---|---|---|
| source / index | SourceData, number | One row in the expanded list. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add sources# or, with the registry in components.jsonnpx shadcn@latest add @prodular/sources