# Citation

> 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.

- **Slug:** `citation` · **Category:** conversation · **Status:** beta
- **Import:** `import { Citation, CitationCard, CitationSource, Sources } from "@kura-ui/react/citation";`
- **Docs:** https://kuro.abstergo.fyi/components/citation
- **Registry item:** https://kuro.abstergo.fyi/r/citation.json
- **Tags:** citation, sources, rag, references, footnote, hover-card, search, attribution

## Install

```bash
# shadcn registry (copies source into your project)
npx shadcn@latest add https://kuro.abstergo.fyi/r/citation.json
# Kura CLI (same, plus dependency resolution and --json output)
npx @kura-ui/cli add citation
# npm package
npm install @kura-ui/react
```

## When to use

- 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`).

## When not to use

- 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 | Required | Description |
| --- | --- | --- | --- | --- |
| `index` | `number` | — | yes | 1-based citation number shown in the pill and the card corner. |
| `source` | `CitationSource` | — | yes | { 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"` | no | Pill shows the number, or a favicon + site name (Perplexity style). |
| `openDelay` | `number` | `250` | no | Hover delay in ms before the preview opens (keyboard focus opens immediately). |
| `open` | `boolean` | — | no | Preview open (controlled). |
| `onOpenChange` | `(open: boolean) => void` | — | no | Called when the preview opens or closes. |

Native attributes of the root element, `className` and `ref` are passed through.

## Anatomy

- **Citation** (`data-slot="citation"`) — The pill: a real <a target="_blank"> to the source, `data-state="open" | "closed"`. Sized in em so it scales with the surrounding text.
- **Preview** (`data-slot="citation-preview"`) — The hover card (Kura Popover content, origin-aware pop-in, opens above the pill).
- **CitationCard** (`data-slot="citation-card"`) — Preview body: favicon, site · date, index, title link with an ↗ nudge, 3-line snippet. Reusable on its own.
  - `source`: `CitationSource` — Source to show.
  - `index`: `number` — Number shown in the corner.
- **Favicon** (`data-slot="citation-favicon"`) — 16px favicon or monogram tile (aria-hidden).
- **Sources** (`data-slot="citation-sources"`) — Sources behind an answer. `data-variant`.
  - `sources`: `readonly CitationSource[]` — In citation order (index = position + 1).
  - `variant`: `"strip" | "list"` = `"strip"` — strip = horizontally scrolling chips; list = numbered rows with snippets.
  - `label`: `React.ReactNode` = `"Sources"` — Heading (with a count). null hides it.
- **Source** (`data-slot="citation-source"`) — One <li> in Sources.

## 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"`.

## Examples

### One citation (minimal)

A numbered pill at the end of a sentence.

```tsx
import { Citation, type CitationSource } from "@kura-ui/react/citation";

const SOURCES: CitationSource[] = [
  {
    title: "useOptimistic – React",
    url: "https://react.dev/reference/react/useOptimistic",
    siteName: "React",
    snippet:
      "useOptimistic is a React Hook that lets you optimistically update the UI while an async action is underway.",
  },
  {
    title: "Server Actions and Mutations",
    url: "https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations",
    siteName: "Next.js",
    date: "Jun 2026",
    snippet:
      "Server 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.",
  },
  {
    title: "Optimistic UI patterns, revisited",
    url: "https://www.example.com/blog/optimistic-ui",
    snippet:
      "The 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.",
  },
];

export function CitationMinimal() {
  return (
    <p className="max-w-md text-[15px] text-kura-fg leading-relaxed">
      React exposes a dedicated hook for optimistic updates
      <Citation index={1} source={SOURCES[0]!} />.
    </p>
  );
}
```

### Citations with a sources strip (full)

Number and domain pills, a custom open delay, and the Sources strip below.

```tsx
import { Citation, type CitationSource, Sources } from "@kura-ui/react/citation";

const SOURCES: CitationSource[] = [
  {
    title: "useOptimistic – React",
    url: "https://react.dev/reference/react/useOptimistic",
    siteName: "React",
    snippet:
      "useOptimistic is a React Hook that lets you optimistically update the UI while an async action is underway.",
  },
  {
    title: "Server Actions and Mutations",
    url: "https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations",
    siteName: "Next.js",
    date: "Jun 2026",
    snippet:
      "Server 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.",
  },
  {
    title: "Optimistic UI patterns, revisited",
    url: "https://www.example.com/blog/optimistic-ui",
    snippet:
      "The 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.",
  },
];

export function CitationFull() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-5">
      <p className="text-[15px] text-kura-fg leading-relaxed">
        Render the new count immediately with{" "}
        <code className="font-kura-mono text-sm">useOptimistic</code>
        <Citation index={1} source={SOURCES[0]!} openDelay={150} />, run the mutation in a Server
        Action
        <Citation index={2} source={SOURCES[1]!} display="domain" />, and restore the previous value
        quietly if it fails
        <Citation index={3} source={SOURCES[2]!} />.
      </p>
      <Sources sources={SOURCES} variant="strip" />
    </div>
  );
}
```

### Sources list (variant)

Numbered rows with site, date and snippet.

```tsx
import { type CitationSource, Sources } from "@kura-ui/react/citation";

const SOURCES: CitationSource[] = [
  {
    title: "useOptimistic – React",
    url: "https://react.dev/reference/react/useOptimistic",
    siteName: "React",
    snippet:
      "useOptimistic is a React Hook that lets you optimistically update the UI while an async action is underway.",
  },
  {
    title: "Server Actions and Mutations",
    url: "https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations",
    siteName: "Next.js",
    date: "Jun 2026",
    snippet:
      "Server 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.",
  },
  {
    title: "Optimistic UI patterns, revisited",
    url: "https://www.example.com/blog/optimistic-ui",
    snippet:
      "The 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.",
  },
];

export function CitationSourcesList() {
  return (
    <Sources sources={SOURCES} variant="list" label="Further reading" className="w-full max-w-lg" />
  );
}
```

### In an assistant message (composition)

Pills inside a Message with a label-less strip.

```tsx
import { Message, MessageAvatar } from "@kura-ui/react/message";
import { Citation, type CitationSource, Sources } from "@kura-ui/react/citation";

const SOURCES: CitationSource[] = [
  {
    title: "useOptimistic – React",
    url: "https://react.dev/reference/react/useOptimistic",
    siteName: "React",
    snippet:
      "useOptimistic is a React Hook that lets you optimistically update the UI while an async action is underway.",
  },
  {
    title: "Server Actions and Mutations",
    url: "https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations",
    siteName: "Next.js",
    date: "Jun 2026",
    snippet:
      "Server 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.",
  },
  {
    title: "Optimistic UI patterns, revisited",
    url: "https://www.example.com/blog/optimistic-ui",
    snippet:
      "The 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.",
  },
];

export function CitationInMessage() {
  return (
    <Message
      from="assistant"
      name="Kura"
      avatar={<MessageAvatar name="Kura" />}
      className="max-w-xl"
    >
      <p>
        Yes — React 19 ships <code>useOptimistic</code> for exactly this
        <Citation index={1} source={SOURCES[0]!} />. Pair it with a Server Action so the request and
        the rollback live in one place
        <Citation index={2} source={SOURCES[1]!} />.
      </p>
      <Sources sources={SOURCES.slice(0, 2)} label={null} />
    </Message>
  );
}
```

## Dependencies

- npm: `lucide-react`
- Kura registry items: `kura-theme`, `popover`, `use-controllable-state`, `utils`
- Related: `message`, `streaming-text`, `tool-call`
