Skip to content

Citation

Link an answer to its sources with numbered markers and source previews.

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use citations whenever an answer draws on documents, search results or a knowledge base, so people can check a claim without leaving the conversation. Most of the time you don't render them yourself: pass sources to a Chat Message and [1] in the text becomes ChatCitation for sources[0], with ChatSources under the finished answer. Hover or focus a number in the preview to see its source.

import { ChatMessage, type ChatSource } from '@youj/react'

const sources: ChatSource[] = [
  { title: 'Regions and pricing', url: 'https://youj.io/docs/regions', snippet: 'Per-region pricing for functions…' },
]

<ChatMessage role="assistant" content="fra1 is the cheapest at 10M requests a month [1]." sources={sources} />

A ChatSource has a title and a url, with an optional snippet for the preview and an icon (a favicon, say) to replace the domain's monogram. Chat Markdown takes the same sources prop.

Reference

Propsindex, source, defaultOpen… (9)

Props

ChatCitation props
PropType
indexRequirednumber

The number in the text, 1-based.

–
sourceRequiredChatSource–
defaultOpenboolean

Open the preview from the start (a story, a tour).

–

ChatSources

ChatSources props
PropType
sourcesRequiredChatSource[]–
labelstring

default "Sources"

"Sources"
Other props pass through: extends ComponentProps<'div'>.

ChatSource

Each entry in sources:

ChatSource props
PropType
titleRequiredstring

Names the source in the preview, the chip and the marker's label.

–
urlRequiredstring

Opens in a new tab. Its domain shows in the preview and the monogram.

–
snippetstring

A line or two from the source, for the preview.

–
iconReactNode

Replaces the domain's monogram, e.g. a favicon.

–
MotionPreview opens, preview closes, open and close delays… · reduced motion

Motion

Motion tokens
MomentTokens
Preview opensscale .96 → 1 and fade, out of the chip (yj-pop)
duration.fastease.enter

140ms · cubic-bezier(0.16, 1, 0.3, 1)

140mscubic-bezier(0.16, 1, 0.3, 1)
Preview closes
duration.instantease.exit

80ms · cubic-bezier(0.4, 0, 1, 1)

80mscubic-bezier(0.4, 0, 1, 1)
Open and close delayshover intent, not motion; it stays while the pointer travels onto it
no token

500ms, 250ms

500ms, 250ms
Chip hoverbackground and text take the accent
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The preview appears and disappears at once.

Reduced motion

The preview opens and closes without scaling or fading. The hover delays are unchanged, since they're about intent, not motion.

AccessibilityKeyboard, focus

Accessibility

  • Each marker is a link that opens its source in a new tab, named by its number and the source's title ("1, Regions and pricing"). The preview is a sighted convenience: the link carries everything.
  • ChatSources is a list named by its label. A chip that already shows its title isn't named twice.
  • Keyboard focus opens the preview like hover does, and Escape closes it.