Skip to content
Agent UI
Provider composesCopy-paste blockperplexity-composeLive stage

Perplexity Compose

A copy-paste block that recreates the Perplexity prompt stage with Perplexity's rail, brand mark, prompt surface, model menu, and tool toggles. The block is not a package component: copy the file, or install it with the shadcn CLI, then edit the layout like any other file in your project. It is assembled entirely from Agent UI compose primitives, and every control is keyboard accessible.

Source you own: install the block with the shadcn CLI or copy components/blocks/composes/perplexity-compose.tsx. The preview below is the real file, and the code tab is the exact source.

components/blocks/composes/perplexity-compose.tsx

What do you want to know?

Terms of Service · Privacy Notice

The code tab contains the full block, ready to copy.

Installation

Add the block with the shadcn CLI. It writes components/blocks/composes/perplexity-compose.tsx and installs its dependencies.

bash
npx shadcn@latest add https://agent-ui-27t.pages.dev/r/perplexity-compose.json

Prefer to own the file manually? Install the dependencies, then copy the source from the Files section.

bash
pnpm add @agent-ui/react lucide-react

Tailwind CSS v4 users: add @source "../node_modules/@agent-ui/react/src" to the global stylesheet so the library utilities are generated.

Usage

Import the copied file and render it anywhere. The block accepts onSend, disabled, and className; wire onSend to any backend.

tsx
import { PerplexityCompose } from "@/components/blocks/composes/perplexity-compose";
 
<PerplexityCompose onSend={(text) => console.log(text)} />

Files

One file. The same source is served by the shadcn registry endpoint, so the CLI and the docs can never drift.

components/blocks/composes/perplexity-compose.tsxtsx
tsx
"use client";
 
import * as React from "react";
import {
AudioLines,
ChevronDown,
Computer,
Library,
Mic,
PanelLeft,
Plus,
Puzzle,
Search,
Workflow,
} from "lucide-react";
import {
ComposeDisclaimer,
ComposeSendButton,
ComposeTextarea,
ModelChip,
createCompose,
useCompose,
type ComposeProps,
} from "@agent-ui/react";
 
const Root = createCompose({
id: "perplexity",
maxWidth: "max-w-none",
placeholder: "Ask anything…",
models: [
{ id: "sonar-pro", name: "Sonar Pro", hint: "Deep research" },
{ id: "sonar", name: "Sonar", hint: "Fast answers" },
{ id: "sonar-reasoning", name: "Sonar Reasoning", hint: "Reasoning" },
],
initialToggles: { search: true },
});
 
function PerplexityMark({ className }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" className={className} aria-hidden="true" fill="none">
<path
d="M12 2v20M2 12h20M4.9 4.9l14.2 14.2M19.1 4.9 4.9 19.1"
stroke="currentColor"
strokeWidth="1.6"
/>
<path
d="M12 2 8 6h8l-4-4Zm0 20 4-4H8l4 4ZM2 12l4-4v8l-4-4Zm20 0-4 4V8l4 4Z"
fill="currentColor"
/>
</svg>
);
}
 
/** Perplexity: oatmeal rail with teammate promo, answer-first canvas, segmented tools. */
function PerplexityComposeLayout() {
const { toggles, toggle, send } = useCompose();
return (
<div className="flex min-h-[400px] w-full bg-white text-[13px] text-[#13343b]">
<aside className="hidden w-[176px] shrink-0 flex-col @[560px]:flex bg-[#f3f3ee] px-2.5 py-3">
<div className="flex items-center gap-2 px-1">
<PerplexityMark className="h-4 w-4" />
<Search className="ml-1 h-3.5 w-3.5 opacity-60" aria-hidden="true" />
<PanelLeft className="ml-auto h-4 w-4 opacity-60" aria-hidden="true" />
</div>
<nav className="mt-4 flex flex-col gap-0.5">
{[
{ icon: Plus, label: "New" },
{ icon: Computer, label: "Computer" },
{ icon: Workflow, label: "Automations" },
{ icon: Library, label: "Artifacts" },
{ icon: Puzzle, label: "Plugins" },
].map(({ icon: Icon, label }) => (
<button
key={label}
type="button"
className="flex items-center gap-2.5 rounded-lg px-1.5 py-[6px] text-left text-[12px] hover:bg-black/[0.05]"
>
<Icon className="h-[15px] w-[15px] opacity-75" aria-hidden="true" />
{label}
</button>
))}
</nav>
 
<div className="mt-4 flex items-center justify-between px-1.5 text-[10.5px] font-medium text-[#13343b]/60">
Teammates <ChevronDown className="h-3 w-3" aria-hidden="true" />
</div>
<div className="mt-1.5 rounded-xl border border-black/[0.08] bg-white p-2.5 shadow-[0_1px_2px_rgba(0,0,0,0.03)]">
<div className="flex items-start justify-between">
<p className="text-[11px] font-semibold leading-snug">Hire your first teammate</p>
<span className="flex -space-x-1" aria-hidden="true">
<span className="h-3 w-3 rounded-full bg-[#f4c95d]" />
<span className="h-3 w-3 rounded-full bg-[#c084fc]" />
<span className="h-3 w-3 rounded-full bg-[#42b8a4]" />
</span>
</div>
<p className="mt-1 text-[9.5px] leading-snug text-[#13343b]/55">
Teammates are persistent agents that work for you.
</p>
<button
type="button"
className="mt-2 w-full rounded-md bg-[#e8e8e3] py-1 text-[10px] font-medium"
>
Create new teammate
</button>
</div>
 
<div className="mt-3 flex items-center justify-between px-1.5 text-[10.5px] font-medium text-[#13343b]/60">
Projects <ChevronDown className="h-3 w-3" aria-hidden="true" />
</div>
<p className="px-1.5 py-1 text-[10px] text-[#13343b]/40">No projects</p>
<div className="mt-2 flex items-center justify-between px-1.5 text-[10.5px] font-medium text-[#13343b]/60">
Recents <ChevronDown className="h-3 w-3" aria-hidden="true" />
</div>
<p className="px-1.5 py-1 text-[10px] text-[#13343b]/40">No recent sessions</p>
 
<div className="mt-auto flex flex-col gap-2">
<button
type="button"
className="w-full rounded-md border border-black/10 bg-transparent py-1 text-[10.5px] font-medium"
>
View plans
</button>
<button type="button" className="flex items-center gap-1.5 px-1 text-[12px] font-medium">
<span className="grid h-5 w-5 place-items-center rounded-full bg-[#20808d] text-[9px] text-white">
P
</span>
Sign In
</button>
</div>
</aside>
 
<div className="relative flex min-w-0 flex-1 flex-col items-center px-5 pb-7 pt-8">
<PerplexityMark className="mt-auto h-6 w-6 text-[#13343b]" />
<h1 className="mt-3 text-center text-[24px] font-normal tracking-[-0.01em] text-[#13343b]">
What do you want to know?
</h1>
 
<div
data-agent-id="compose-card"
className="mt-4 flex w-full max-w-[470px] flex-col rounded-2xl border border-black/[0.08] bg-white p-3 shadow-[0_2px_10px_rgba(19,52,59,0.05)] transition-[border-color,box-shadow] duration-200 focus-within:border-[#20808d]/35 focus-within:shadow-[0_4px_20px_rgba(32,128,141,0.12)]"
>
<ComposeTextarea
aria-label="Ask anything"
placeholder="Ask anything…"
rows={1}
className="min-h-[38px] px-0.5 py-1 text-[14.5px] leading-6 text-[#13343b] placeholder:text-[#13343b]/35"
/>
<div className="mt-2 flex items-center justify-between gap-2">
<div className="flex min-w-0 items-center gap-1.5">
<button
type="button"
aria-label="Attach"
className="grid h-7 w-7 place-items-center rounded-full text-[#13343b]/70 hover:bg-black/5"
>
<Plus className="h-4 w-4" aria-hidden="true" />
</button>
<div className="flex items-center rounded-full bg-[#f3f3ee] p-0.5">
<button
type="button"
aria-label="Toggle web search"
aria-pressed={toggles.search}
onClick={() => toggle("search")}
className="flex items-center gap-1 rounded-full bg-white px-2 py-1 text-[11px] font-medium shadow-[0_1px_2px_rgba(0,0,0,0.08)] transition-opacity aria-pressed:opacity-100 opacity-60"
>
<Search className="h-3 w-3" aria-hidden="true" /> Search
</button>
<button
type="button"
aria-label="Computer mode"
className="flex items-center gap-1 rounded-full px-2 py-1 text-[11px] text-[#13343b]/60"
>
<Computer className="h-3 w-3" aria-hidden="true" /> Computer
</button>
</div>
</div>
<div className="flex shrink-0 items-center gap-1">
<ModelChip
tone="light"
className="[&>button]:h-7 [&>button]:px-1 [&>button]:text-[11px] [&>button]:text-[#13343b]/60 [&>button]:hover:bg-transparent"
/>
<button
type="button"
aria-label="Dictation"
className="grid h-7 w-7 place-items-center rounded-full text-[#13343b]/60 hover:bg-black/5"
>
<Mic className="h-3.5 w-3.5" aria-hidden="true" />
</button>
<ComposeSendButton
aria-label="Send message"
className="h-7 w-7 bg-[#13343b] text-white hover:bg-[#13343b]/85 disabled:bg-[#13343b] disabled:text-white disabled:opacity-100"
icon={<AudioLines className="h-3.5 w-3.5" strokeWidth={2.4} />}
/>
</div>
</div>
</div>
 
<div className="mt-3 grid w-full max-w-[470px] grid-cols-2 gap-2">
<button
type="button"
onClick={() => send("Search anything")}
className="rounded-xl bg-[#f6f6f2] p-3 text-left transition-colors hover:bg-[#eeeeea]"
>
<p className="flex items-center gap-1.5 text-[11.5px] font-medium">
<Search className="h-3.5 w-3.5" aria-hidden="true" /> Search anything
</p>
<p className="mt-1 text-[10px] leading-snug text-[#13343b]/55">
Get fast and accurate answers from the most trusted sources.
</p>
</button>
<button
type="button"
onClick={() => send("Get work done with Computer")}
className="rounded-xl bg-[#eef4f2] p-3 text-left transition-colors hover:bg-[#e5efec]"
>
<p className="flex items-center gap-1.5 text-[11.5px] font-medium">
<Computer className="h-3.5 w-3.5" aria-hidden="true" /> Get work done with Computer
<span className="rounded bg-[#d7efe9] px-1 py-px text-[8px] font-bold uppercase text-[#20808d]">
New
</span>
</p>
<p className="mt-1 text-[10px] leading-snug text-[#13343b]/55">
Hand off your projects to get polished, reliable deliverables around the clock.
</p>
</button>
</div>
 
<ComposeDisclaimer className="mt-auto pt-5 text-[9.5px] text-[#13343b]/50">
<a href="#" className="underline">
Terms of Service
</a>{" "}
·{" "}
<a href="#" className="underline">
Privacy Notice
</a>
</ComposeDisclaimer>
</div>
</div>
);
}
 
export function PerplexityCompose(props: ComposeProps) {
return (
<Root {...props}>
<PerplexityComposeLayout />
</Root>
);
}
 

Built with

The block imports these Agent UI primitives. Each one is a package export with its own docs page, so you can change the layout without giving up the behaviour.

Accessibility

All controls expose accessible names; the textarea supports Enter to send and Shift+Enter for a new line.

Perplexity also has a full product page.

Metadata, model list, and the same block in a dedicated route.

Open Perplexity
Use it with the rest of the library

PerplexityCompose composes with MessageList, ChatShell, useChat, and the provider compose blocks. Browse the full reference or see all components in the gallery.

248 lines · copy-ready