Join our Discord Community

React AI Sources

Collapsible citation display for AI-generated content. Build transparent AI interfaces with React, Next.js, and TypeScript, featuring expandable source links and customizable citation counts for shadcn/ui applications.

Trying to implement AI Elements?

Join our Discord community for help from other developers.


Collapsible source citations for AI responses with React, Next.js, and TypeScript. Because when your AI claims something cost $50M to build, users want to see the actual press release, not just trust the chatbot. This free open source shadcn/ui component provides transparent AI citations for your conversational AI applications using Vercel AI SDK in JavaScript frameworks.

Collapsible source citations

Expandable list with source links:

Loading component...

The React component provides a clean trigger showing source count, smooth expand/collapse animations, and clickable source links that open in new tabs. Built with TypeScript for type safety and designed for seamless integration in your JavaScript projects.

Installation

npx shadcn@latest add https://www.shadcn.io/registry/ai.json
npx shadcn@latest add https://www.shadcn.io/registry/ai.json
pnpm dlx shadcn@latest add https://www.shadcn.io/registry/ai.json
bunx shadcn@latest add https://www.shadcn.io/registry/ai.json

Usage

import {
  Sources,
  SourcesTrigger,
  SourcesContent,
  Source,
} from "@/components/ai/sources";

<Sources>
  <SourcesTrigger count={3} />
  <SourcesContent>
    <Source href="https://example.com/doc1" title="Documentation" />
    <Source href="https://example.com/research" title="Research Paper" />
    <Source href="https://example.com/blog" title="Blog Article" />
  </SourcesContent>
</Sources>;

Why show sources at all?

AI models hallucinate confidently in conversational AI applications. They'll cite studies that don't exist and quote statistics from nowhere in React projects. Sources let users fact-check the claims that matter in AI chat applications.

Collapsible design works because most people trust AI responses in TypeScript implementations, but skeptical users (the smart ones) want to verify everything. Give them the option without cluttering your Next.js AI application UI in JavaScript frameworks.

Usage with AI SDK

Display sources from search-enabled models in React applications:

"use client";

import {
  Sources,
  SourcesTrigger,
  SourcesContent,
  Source,
} from "@/components/ai/sources";
import {
  Conversation,
  ConversationContent,
} from "@/components/ai/conversation";
import { Message, MessageContent } from "@/components/ai/message";
import {
  PromptInput,
  PromptInputTextarea,
  PromptInputSubmit,
} from "@/components/ai/prompt-input";
import { Response } from "@/components/ai/response";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";

export default function SourcesChat() {
  const [input, setInput] = useState("");
  const { messages, append, status } = useChat({
    api: "/api/sources",
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (input.trim()) {
      append({ role: "user", content: input });
      setInput("");
    }
  };

  return (
    <div className="flex flex-col h-full max-w-4xl mx-auto">
      <Conversation>
        <ConversationContent>
          {messages.map((message) => (
            <div key={message.id}>
              {message.role === "assistant" && message.sources && (
                <Sources>
                  <SourcesTrigger count={message.sources.length} />
                  <SourcesContent>
                    {message.sources.map((source, i) => (
                      <Source
                        key={i}
                        href={source.url}
                        title={source.title || source.url}
                      />
                    ))}
                  </SourcesContent>
                </Sources>
              )}

              <Message from={message.role}>
                <MessageContent>
                  <Response>{message.content}</Response>
                </MessageContent>
              </Message>
            </div>
          ))}
        </ConversationContent>
      </Conversation>

      <PromptInput onSubmit={handleSubmit}>
        <PromptInputTextarea
          value={input}
          onChange={(e) => setInput(e.currentTarget.value)}
          placeholder="Ask a question with sources..."
        />
        <PromptInputSubmit disabled={!input.trim()} status={status} />
      </PromptInput>
    </div>
  );
}

Backend route with source-enabled model:

// app/api/sources/route.ts
import { streamText, convertToModelMessages } from "ai";
import { perplexity } from "@ai-sdk/perplexity";

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: "perplexity/sonar",
    system:
      "You are a helpful assistant. Always use search to provide sources.",
    messages: convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse({
    sendSources: true, // Enable source streaming
  });
}

Examples

Custom source rendering

Override default book icon and styling:

Loading component...

Features

  • Collapsible container with smooth Radix UI animations in React applications
  • Source count display in trigger button for TypeScript components
  • Animated chevron icon showing open/closed state in JavaScript frameworks
  • Individual source links with book icons for Next.js projects
  • Opens sources in new tabs with proper rel attributes
  • Accessible keyboard navigation
  • Responsive design adapting to different screen sizes
  • Theme-aware styling for light and dark modes
  • Perfect for AI search results, AI chat applications, research citations, and fact-checking
  • Free open source component built for Next.js with shadcn/ui design system and AI Elements
  • Optimized for Vercel AI SDK streaming and conversational AI transparency

API Reference

Sources

Container component managing collapsible state.

PropTypeDescription
classNamestringAdditional CSS classes
...propsComponentProps<'div'>Standard div attributes

SourcesTrigger

Clickable trigger showing source count.

PropTypeDefaultDescription
countnumber-Required - Number of sources
classNamestring-Additional CSS classes
childrenReactNode-Custom trigger content
...propsComponentProps<typeof CollapsibleTrigger>-Collapsible trigger props

SourcesContent

Container for source links with animations.

PropTypeDescription
classNamestringAdditional CSS classes
...propsComponentProps<typeof CollapsibleContent>Collapsible content props

Source

Individual source link with icon.

PropTypeDescription
hrefstringSource URL
titlestringSource title or description
childrenReactNodeCustom source content
...propsComponentProps<'a'>Standard anchor attributes

Keyboard interactions

KeyDescription
Space / EnterToggle sources visibility on trigger
TabNavigate between trigger and source links
EnterOpen source link (when focused)

Production tips

Validate source URLs. Always ensure URLs are valid and safe before rendering in React applications. Consider domain allowlists for security in TypeScript projects.

Limit source count. Too many sources overwhelm users in JavaScript implementations. 3-5 sources usually suffice for most responses in Next.js applications.

Provide meaningful titles. Don't just show URLs—extract page titles or provide descriptive labels in React components.

Consider mobile space. Collapsible design helps, but test source display on small screens in TypeScript frameworks.

Track source clicks. Analytics on which sources users verify helps improve AI trustworthiness in JavaScript applications.

Handle missing sources gracefully. If no sources available, consider hiding the component entirely in React projects.

Integration with other components

Sources works seamlessly with Response for cited content in React applications. Use alongside Message for chat interfaces with sources in Next.js projects. Combine with InlineCitation for inline references. This free open source component integrates seamlessly with modern JavaScript frameworks.

Questions developers actually ask