• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
drewmcdonald

drewmcdonald

promptCompare

Public
Like
promptCompare
Home
Code
7
.claude
3
backend
2
docs
1
frontend
4
shared
.mcp.json
deno.json
Environment variables
2
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
.claude
/
skills
/
ai-elements
/
references
/
sources.md
Code
/
.claude
/
skills
/
ai-elements
/
references
/
sources.md
Search
…
Viewing readonly version of main branch: v131
View latest version
sources.md

Sources

A component that allows a user to view the sources or citations used to generate a response.

The Sources component allows a user to view the sources or citations used to generate a response.

See scripts/sources.tsx for this example.

Installation

npx ai-elements@latest add sources

Usage with AI SDK

Build a simple web search agent with Perplexity Sonar.

Add the following component to your frontend:

Create val
"use client"; import { useChat } from "@ai-sdk/react"; import { Source, Sources, SourcesContent, SourcesTrigger, } from "@/components/ai-elements/sources"; import { Input, PromptInputSubmit, PromptInputTextarea, } from "@/components/ai-elements/prompt-input"; import { Conversation, ConversationContent, ConversationScrollButton, } from "@/components/ai-elements/conversation"; import { Message, MessageContent, MessageResponse, } from "@/components/ai-elements/message"; import { useState } from "react"; import { DefaultChatTransport } from "ai"; const SourceDemo = () => { const [input, setInput] = useState(""); const { messages, sendMessage, status } = useChat({ transport: new DefaultChatTransport({ api: "/api/sources", }), }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (input.trim()) { sendMessage({ text: input }); setInput(""); } }; return ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <div className="flex flex-col h-full"> <div className="flex-1 overflow-auto mb-4"> <Conversation> <ConversationContent> {messages.map((message) => ( <div key={message.id}> {message.role === "assistant" && ( <Sources> <SourcesTrigger count={message.parts.filter( (part) => part.type === "source-url", ).length} /> {message.parts.map((part, i) => { switch (part.type) { case "source-url": return ( <SourcesContent key={`${message.id}-${i}`}> <Source key={`${message.id}-${i}`} href={part.url} title={part.url} /> </SourcesContent> ); } })} </Sources> )} <Message from={message.role} key={message.id}> <MessageContent> {message.parts.map((part, i) => { switch (part.type) { case "text": return ( <MessageResponse key={`${message.id}-${i}`}> {part.text} </MessageResponse> ); default: return null; } })} </MessageContent> </Message> </div> ))} </ConversationContent> <ConversationScrollButton /> </Conversation> </div> <Input onSubmit={handleSubmit} className="mt-4 w-full max-w-2xl mx-auto relative" > <PromptInputTextarea value={input} placeholder="Ask a question and search the..." onChange={(e) => setInput(e.currentTarget.value)} className="pr-12" /> <PromptInputSubmit status={status === "streaming" ? "streaming" : "ready"} disabled={!input.trim()} className="absolute bottom-1 right-1" /> </Input> </div> </div> ); }; export default SourceDemo;

Add the following route to your backend:

Create val
import { convertToModelMessages, streamText, UIMessage } from "ai"; import { perplexity } from "@ai-sdk/perplexity"; // Allow streaming responses up to 30 seconds export const maxDuration = 30; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: "perplexity/sonar", system: "You are a helpful assistant. Keep your responses short (< 100 words) unless you are asked for more details. ALWAYS USE SEARCH.", messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse({ sendSources: true, }); }

Features

  • Collapsible component that allows a user to view the sources or citations used to generate a response
  • Customizable trigger and content components
  • Support for custom sources or citations
  • Responsive design with mobile-friendly controls
  • Clean, modern styling with customizable themes

Examples

Custom rendering

See scripts/sources-custom.tsx for this example.

Props

<Sources />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the root div.

<SourcesTrigger />

PropTypeDefaultDescription
countnumberRequiredThe number of sources to display in the trigger.
...propsReact.ComponentProps<typeof CollapsibleTrigger>-Any other props are spread to the CollapsibleTrigger component.

<SourcesContent />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the content container.

<Source />

PropTypeDefaultDescription
...propsReact.AnchorHTMLAttributes<HTMLAnchorElement>-Any other props are spread to the anchor element.
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.