Citation
betaInline numbered citation pill that links to its source and previews favicon, title, domain and snippet on hover/focus, plus a Sources strip or list.
One citation
minimal
React exposes a dedicated hook for optimistic updates1.
Citations with a sources strip
Number and domain pills, a custom open delay, and the Sources strip below.
Sources list
Numbered rows with site, date and snippet.
variant
Further reading3
- 1useOptimistic – ReactReactuseOptimistic is a React Hook that lets you optimistically update the UI while an async action is underway.
- 2Server Actions and MutationsNext.js· Jun 2026Server Actions are asynchronous functions that are executed on the server. They can be called in Server and Client Components to handle form submissions and data mutations.
- 3Optimistic UI patterns, revisitedexample.comThe trick is not the optimism itself but the rollback: show the failure where the user is looking, and restore the previous value without a jump.
In an assistant message
Pills inside a Message with a label-less strip.
composition
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/citation.jsonCopies the source into components/kura/citation with its dependencies (kura-theme, popover, use-controllable-state, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Attribute sentences of a model's answer to retrieved sources (RAG, web search, file search) with inline [1] [2] pills.
- Preview a source without leaving the answer: hover or keyboard-focus the pill to see title, domain and the snippet used.
- List all sources behind an answer under it, as a compact chip strip or a numbered list (`Sources`).
Reach for something else when
- Plain links in prose with no source metadata — use an <a>.
- Footnotes in long-form documents that must print — use real footnotes.
- Showing the search tool call itself (query, results JSON) — use tool-call.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| index* | number | — | 1-based citation number shown in the pill and the card corner. |
| source* | CitationSource | — | { title, url, snippet?, favicon?, siteName?, date? }. The domain is derived from `url`; without `favicon` a monogram tile is shown (no third-party favicon service is called). |
| display | "number" | "domain" | "number" | Pill shows the number, or a favicon + site name (Perplexity style). |
| openDelay | number | 250 | Hover delay in ms before the preview opens (keyboard focus opens immediately). |
| open | boolean | — | Preview open (controlled). |
| onOpenChange | (open: boolean) => void | — | Called when the preview opens or closes. |
All native attributes of the root element, className and ref pass through.
Anatomy
- CitationThe pill: a real <a target="_blank"> to the source, `data-state="open" | "closed"`. Sized in em so it scales with the surrounding text.
data-slot="citation" - PreviewThe hover card (Kura Popover content, origin-aware pop-in, opens above the pill).
data-slot="citation-preview" - CitationCardPreview body: favicon, site · date, index, title link with an ↗ nudge, 3-line snippet. Reusable on its own.
data-slot="citation-card" - Favicon16px favicon or monogram tile (aria-hidden).
data-slot="citation-favicon" - SourcesSources behind an answer. `data-variant`.
data-slot="citation-sources" - SourceOne <li> in Sources.
data-slot="citation-source"
Accessibility
- Each pill is a link named “Source 2: <title> (<domain>)”, so it makes sense out of context and in a links list.
- The preview opens on hover (with delay) and on keyboard focus, without moving focus; Escape or blur closes it. It never contains information that is not also reachable by following the link.
- Sources is an ordered list labelled by its heading.
- Links open in a new tab (`target="_blank" rel="noreferrer"`).
- Reduced motion: The preview only fades (no scale/translate) and the ↗ nudge on hover is removed.
| Keys | Action |
|---|---|
| Tab | Focus a pill — its preview opens |
| Enter | Open the source in a new tab |
| Escape | Close the preview |
Notes for agents
- Place the pill directly after the punctuation-free end of the claim, inside the same text node's parent (it is inline and em-sized).
- Reuse the same index for repeated citations of the same source; Sources numbering is by array position.
- Pass favicons you already have (e.g. from your search API); the component never fetches favicons itself.
- Import Sources from the same entry: `import { Citation, Sources } from "@kura-ui/react/citation"`.