12 KiB
Inline Citation
URL: /components/inline-citation
title: Inline Citation description: A hoverable citation component that displays source information and quotes inline with text, perfect for AI-generated content with references. path: elements/components/inline-citation
The InlineCitation component provides a way to display citations inline with text content, similar to academic papers or research documents. It consists of a citation pill that shows detailed source information on hover, making it perfect for AI-generated content that needs to reference sources.
Installation
Usage
import {
InlineCitation,
InlineCitationCard,
InlineCitationCardBody,
InlineCitationCardTrigger,
InlineCitationCarousel,
InlineCitationCarouselContent,
InlineCitationCarouselItem,
InlineCitationCarouselHeader,
InlineCitationCarouselIndex,
InlineCitationSource,
InlineCitationText,
} from "@/components/ai-elements/inline-citation";
<InlineCitation>
<InlineCitationText>{citation.text}</InlineCitationText>
<InlineCitationCard>
<InlineCitationCardTrigger sources={citation.sources.map((source) => source.url)} />
<InlineCitationCardBody>
<InlineCitationCarousel>
<InlineCitationCarouselHeader>
<InlineCitationCarouselIndex />
</InlineCitationCarouselHeader>
<InlineCitationCarouselContent>
<InlineCitationCarouselItem>
<InlineCitationSource title="AI SDK" url="https://ai-sdk.dev" description="The AI Toolkit for TypeScript" />
</InlineCitationCarouselItem>
</InlineCitationCarouselContent>
</InlineCitationCarousel>
</InlineCitationCardBody>
</InlineCitationCard>
</InlineCitation>
Usage with AI SDK
Build citations for AI-generated content using experimental_generateObject.
Add the following component to your frontend:
"use client";
import { experimental_useObject as useObject } from "@ai-sdk/react";
import {
InlineCitation,
InlineCitationText,
InlineCitationCard,
InlineCitationCardTrigger,
InlineCitationCardBody,
InlineCitationCarousel,
InlineCitationCarouselContent,
InlineCitationCarouselItem,
InlineCitationCarouselHeader,
InlineCitationCarouselIndex,
InlineCitationCarouselPrev,
InlineCitationCarouselNext,
InlineCitationSource,
InlineCitationQuote,
} from "@/components/ai-elements/inline-citation";
import { Button } from "@/components/ui/button";
import { citationSchema } from "@/app/api/citation/route";
const CitationDemo = () => {
const { object, submit, isLoading } = useObject({
api: "/api/citation",
schema: citationSchema,
});
const handleSubmit = (topic: string) => {
submit({ prompt: topic });
};
return (
<div className="max-w-4xl mx-auto p-6 space-y-6">
<div className="flex gap-2 mb-6">
<Button onClick={() => handleSubmit("artificial intelligence")} disabled={isLoading} variant="outline">
Generate AI Content
</Button>
<Button onClick={() => handleSubmit("climate change")} disabled={isLoading} variant="outline">
Generate Climate Content
</Button>
</div>
{isLoading && !object && <div className="text-muted-foreground">Generating content with citations...</div>}
{object?.content && (
<div className="prose prose-sm max-w-none">
<p className="leading-relaxed">
{object.content.split(/(\[\d+\])/).map((part, index) => {
const citationMatch = part.match(/\[(\d+)\]/);
if (citationMatch) {
const citationNumber = citationMatch[1];
const citation = object.citations?.find((c: any) => c.number === citationNumber);
if (citation) {
return (
<InlineCitation key={index}>
<InlineCitationCard>
<InlineCitationCardTrigger sources={[citation.url]} />
<InlineCitationCardBody>
<InlineCitationCarousel>
<InlineCitationCarouselHeader>
<InlineCitationCarouselPrev />
<InlineCitationCarouselNext />
<InlineCitationCarouselIndex />
</InlineCitationCarouselHeader>
<InlineCitationCarouselContent>
<InlineCitationCarouselItem>
<InlineCitationSource
title={citation.title}
url={citation.url}
description={citation.description}
/>
{citation.quote && <InlineCitationQuote>{citation.quote}</InlineCitationQuote>}
</InlineCitationCarouselItem>
</InlineCitationCarouselContent>
</InlineCitationCarousel>
</InlineCitationCardBody>
</InlineCitationCard>
</InlineCitation>
);
}
}
return part;
})}
</p>
</div>
)}
</div>
);
};
export default CitationDemo;
Add the following route to your backend:
import { streamObject } from "ai";
import { z } from "zod";
export const citationSchema = z.object({
content: z.string(),
citations: z.array(
z.object({
number: z.string(),
title: z.string(),
url: z.string(),
description: z.string().optional(),
quote: z.string().optional(),
})
),
});
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { prompt } = await req.json();
const result = streamObject({
model: "openai/gpt-4o",
schema: citationSchema,
prompt: `Generate a well-researched paragraph about ${prompt} with proper citations.
Include:
- A comprehensive paragraph with inline citations marked as [1], [2], etc.
- 2-3 citations with realistic source information
- Each citation should have a title, URL, and optional description/quote
- Make the content informative and the sources credible
Format citations as numbered references within the text.`,
});
return result.toTextStreamResponse();
}
Features
- Hover interaction to reveal detailed citation information
- Carousel navigation for multiple citations with prev/next controls
- Live index tracking showing current slide position (e.g., "1/5")
- Support for source titles, URLs, and descriptions
- Optional quote blocks for relevant excerpts
- Composable architecture for flexible citation formats
- Accessible design with proper keyboard navigation
- Seamless integration with AI-generated content
- Clean visual design that doesn't disrupt reading flow
- Smart badge display showing source hostname and count
Props
<InlineCitation />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the root span element.', type: 'React.ComponentProps<"span">', }, }} />
<InlineCitationText />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying span element.', type: 'React.ComponentProps<"span">', }, }} />
<InlineCitationCard />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the HoverCard component.', type: 'React.ComponentProps<"span">', }, }} />
<InlineCitationCardTrigger />
<TypeTable type={{ sources: { description: 'Array of source URLs. The length determines the number displayed in the badge.', type: 'string[]', }, '...props': { description: 'Any other props are spread to the underlying button element.', type: 'React.ComponentProps<"button">', }, }} />
<InlineCitationCardBody />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying div.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationCarousel />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying Carousel component.', type: 'React.ComponentProps', }, }} />
<InlineCitationCarouselContent />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying CarouselContent component.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationCarouselItem />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying div.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationCarouselHeader />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying div.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationCarouselIndex />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying div. Children will override the default index display.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationCarouselPrev />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying CarouselPrevious component.', type: 'React.ComponentProps', }, }} />
<InlineCitationCarouselNext />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying CarouselNext component.', type: 'React.ComponentProps', }, }} />
<InlineCitationSource />
<TypeTable type={{ title: { description: 'The title of the source.', type: 'string', }, url: { description: 'The URL of the source.', type: 'string', }, description: { description: 'A brief description of the source.', type: 'string', }, '...props': { description: 'Any other props are spread to the underlying div.', type: 'React.ComponentProps<"div">', }, }} />
<InlineCitationQuote />
<TypeTable type={{ '...props': { description: 'Any other props are spread to the underlying blockquote element.', type: 'React.ComponentProps<"blockquote">', }, }} />