16 / 37 · Knowledge
Sources
Citations collapsed into a pill, expanding into scannable source cards.
Installation
npx shadcn@latest add "@assistant-ui/elements-sources"
Usage
import { Sources } from "@/components/elements/sources";
<Sources
sources={[{ domain: "docs.example.com", title: "Composer guide" }]}
open
onOpenChange={setOpen}
/>Props
sources*readonly Source[]Citation cards revealed when the sources pill expands.
open*booleanWhether the source list is expanded under the pill.
onOpenChange*(open: boolean) => voidCalled when the user expands or collapses the sources.
classNamestringExtra classes merged onto the root.
Source
sources.tsx"use client";
import { ChevronDownIcon } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { cn } from "@/lib/utils";
import { collapsePanel, fieldInteractive, mono, paper } from "./surfaces";
export interface Source {
domain: string;
title: string;
}
export interface SourcesProps {
sources: readonly Source[];
open: boolean;
onOpenChange: (open: boolean) => void;
className?: string;
}
export function Sources({
sources,
open,
onOpenChange,
className,
}: SourcesProps) {
return (
<Collapsible
open={open}
onOpenChange={onOpenChange}
className={cn("w-full max-w-sm", className)}
>
<CollapsibleTrigger
className={cn(
fieldInteractive,
"group/trigger text-foreground/60 hover:text-foreground/90 inline-flex w-fit items-center gap-1.5 rounded-full px-3.5 py-2 text-xs outline-none",
)}
>
<span>Sources</span>
<span className={cn(mono, "text-foreground/35 tabular-nums")}>
{sources.length}
</span>
<ChevronDownIcon className="size-3 opacity-60 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-open/trigger:rotate-180 group-data-panel-open/trigger:rotate-180 motion-reduce:transition-none" />
</CollapsibleTrigger>
<CollapsibleContent className={cn(collapsePanel, "outline-none")}>
<div className="grid grid-cols-2 gap-2 pt-2.5">
{sources.map((source) => (
<div
key={source.domain}
className={cn(
paper,
"flex flex-col gap-1.5 rounded-2xl p-3 transition-transform hover:-translate-y-px",
)}
>
<div className="flex items-center gap-1.5">
<span className="bg-foreground/[0.06] text-foreground/45 flex size-4 shrink-0 items-center justify-center rounded text-[9px] font-medium">
{source.domain.charAt(0).toUpperCase()}
</span>
<span className={cn(mono, "text-foreground/40 truncate")}>
{source.domain}
</span>
</div>
<span className="text-foreground/90 line-clamp-2 text-[13px] leading-snug font-medium">
{source.title}
</span>
</div>
))}
</div>
</CollapsibleContent>
</Collapsible>
);
}