Skip to content

Docs

Components

Sources

Citations as numbered chips or cards, linked from [n] markers in the answer.

Example

Interactive, and the same on the components page. Try the keyboard below on it.

Installation

npm i signoff-ui ai
import { Sources, getSourceParts } from 'signoff-ui';
// Once per app (with Tailwind v4: @import 'signoff-ui/tailwind.css'; in your CSS)
import 'signoff-ui/styles.css';

The styles are once per app; Getting started has the Tailwind v4 and plain CSS options.

Usage

// Send them from the server with toUIMessageStreamResponse({ sendSources: true }).
<Sources sources={getSourceParts(message.parts)} variant="cards" />

How it works

source-url and source-document parts as numbered chips or cards. In AgentMessage, [n] markers in the text become links to the matching source. It has no state, so it renders on the server too.

API reference

Generated from the types the package ships, so it matches the version you install.

Sources

Citation list as compact chips or richer cards. External links open in a new tab.

Props

sourcesrequired
ReadonlyArray<SourcePart | SourceItem>
variant
'chips' | 'cards'Default 'chips'
label
string | null

Visible heading. Default "Sources" (labels.sources.label); pass null to hide it (it stays as the accessible name).

idPrefix
stringDefault 'source'

Prefix for element ids (${idPrefix}-1, …) so inline citations can link to a source.

labels
SignoffLabelsInput

Words to use instead of the English defaults: see SignoffLabelsProvider.

Other props go to the root <div>: className (merged with tailwind-merge), id, aria-*, data-* and event handlers.

SourceItem

A source in app-level shape. AI SDK source-url / source-document parts are accepted too.

idrequired
string
url
string
title
string
description
string

Optional snippet shown in the cards variant.

filename
string

For documents.

mediaType
string

Accessibility

  • An ordered list named by label ("Sources"), not a landmark, so a long conversation doesn’t flood landmark navigation.
  • Links open in a new tab and say so to screen readers: "(opens in a new tab)".
  • Each item has the id ${idPrefix}-${n}, which [n] citation links in Markdown and AgentMessage point to.
  • It has no state and no 'use client', so it renders in Server Components too.

Theming

Every color, radius and font is a CSS variable, so the overrides in Theming apply, on :root or scoped to any element. These hooks are read from the component's markup.

Slots (data-slot)
signoff-sources
State attributes
data-varianton signoff-sources'chips' | 'cards'
Tokens it uses
--signoff-accent-fg--signoff-border--signoff-border-strong--signoff-fg--signoff-fg-muted--signoff-fg-subtle--signoff-font-mono--signoff-font-sans--signoff-ring--signoff-surface--signoff-surface-2
app/globals.css
/* Only this component, and only inside .settings-panel */
.settings-panel [data-slot='signoff-sources'] {
  --signoff-radius: 4px;
}

[data-slot='signoff-sources'][data-variant='chips'] {
  outline: 1px solid var(--signoff-accent);
}