Skip to content
Kura UI

Citation

beta

Inline numbered citation pill that links to its source and previews favicon, title, domain and snippet on hover/focus, plus a Sources strip or list.

@kura-ui/react/citationCopy page for agents.mdregistry.json

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.

full

Render the new count immediately with useOptimistic1, run the mutation in a Server ActionNext.js, and restore the previous value quietly if it fails3.

Sources list

Numbered rows with site, date and snippet.

In an assistant message

Pills inside a Message with a label-less strip.

composition
Kura
Kura

Yes — React 19 ships useOptimistic for exactly this1. Pair it with a Server Action so the request and the rollback live in one place2.

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/citation.json

Copies 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

PropTypeDefaultDescription
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).
openDelaynumber250Hover delay in ms before the preview opens (keyboard focus opens immediately).
openboolean—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

  1. 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"
  2. PreviewThe hover card (Kura Popover content, origin-aware pop-in, opens above the pill).data-slot="citation-preview"
  3. CitationCardPreview body: favicon, site · date, index, title link with an ↗ nudge, 3-line snippet. Reusable on its own.data-slot="citation-card"
  4. Favicon16px favicon or monogram tile (aria-hidden).data-slot="citation-favicon"
  5. SourcesSources behind an answer. `data-variant`.data-slot="citation-sources"
  6. 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.
KeysAction
TabFocus a pill — its preview opens
EnterOpen the source in a new tab
EscapeClose 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"`.