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

ChatGPT Compose

A copy-paste block that recreates the ChatGPT prompt stage with OpenAI'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/openai-compose.tsx. The preview below is the real file, and the code tab is the exact source.

components/blocks/composes/openai-compose.tsx

Where should we begin?

ChatGPT is AI. By using it, you agree to our Terms & Privacy Policy.

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

Installation

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

bash
npx shadcn@latest add https://agent-ui-27t.pages.dev/r/openai-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 { OpenAICompose } from "@/components/blocks/composes/openai-compose";
 
<OpenAICompose 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/openai-compose.tsxtsx
tsx
"use client";
 
import * as React from "react";
import {
ArrowUp,
ChevronDown,
ExternalLink,
Image as ImageIcon,
Mic,
PanelLeft,
Plus,
Puzzle,
Settings,
SquarePen,
Telescope,
} from "lucide-react";
import {
ComposeDisclaimer,
ComposeSendButton,
ComposeTextarea,
createCompose,
useCompose,
type ComposeProps,
} from "@agent-ui/react";
 
const Root = createCompose({
id: "openai",
maxWidth: "max-w-none",
placeholder: "Ask ChatGPT",
});
 
const NAV = [
{ icon: SquarePen, label: "New chat" },
{ icon: ImageIcon, label: "Images" },
{ icon: Puzzle, label: "Plugins" },
{ icon: Telescope, label: "Deep research" },
{ icon: Settings, label: "Settings" },
];
 
/** ChatGPT logged-out home: warm grey rail + airy white canvas + capsule composer. */
function OpenAIComposeLayout() {
const { send } = useCompose();
return (
<div className="flex min-h-[400px] w-full bg-white text-[13px] text-[#0d0d0d]">
<aside className="hidden w-[172px] shrink-0 flex-col @[560px]:flex border-r border-black/[0.06] bg-[#f9f9f9] px-2.5 py-3">
<div className="flex items-center justify-between px-1">
<button
type="button"
className="flex items-center gap-1 text-[13px] font-semibold tracking-tight"
>
ChatGPT <ChevronDown className="h-3 w-3 opacity-60" />
</button>
<PanelLeft className="h-4 w-4 opacity-50" aria-hidden="true" />
</div>
<nav className="mt-4 flex flex-col gap-0.5">
{NAV.map(({ icon: Icon, label }) => (
<button
key={label}
type="button"
className="flex items-center gap-2.5 rounded-lg px-2 py-[6px] text-left text-[12px] text-[#202020] transition-colors hover:bg-black/[0.05]"
>
<Icon className="h-[15px] w-[15px] opacity-80" aria-hidden="true" />
{label}
</button>
))}
<button
type="button"
className="flex items-center gap-2.5 rounded-lg px-2 py-[6px] text-left text-[12px] text-[#202020] transition-colors hover:bg-black/[0.05]"
>
<ExternalLink className="h-[15px] w-[15px] opacity-80" aria-hidden="true" />
See plans and pricing
</button>
</nav>
<div className="mt-auto rounded-xl px-1 pt-4">
<p className="text-[12px] font-semibold leading-snug">Get responses tailored to you</p>
<p className="mt-1 text-[10px] leading-snug text-black/50">
Log in to get answers based on saved chats, plus create images and upload files.
</p>
<button
type="button"
className="mt-2 w-full rounded-full bg-[#ececec] py-1.5 text-[11.5px] font-medium text-[#0d0d0d] transition-colors hover:bg-[#e3e3e3]"
>
Log in
</button>
</div>
</aside>
 
<div className="relative flex min-w-0 flex-1 flex-col items-center justify-center px-5 pb-8 pt-4">
<div className="mb-6 flex items-center gap-2 self-end text-[11px]">
<button
type="button"
className="rounded-full bg-[#0d0d0d] px-3 py-1.5 font-medium text-white"
>
Log in
</button>
<button
type="button"
className="rounded-full border border-black/10 px-3 py-1.5 font-medium"
>
Sign up for free
</button>
</div>
 
<h1 className="text-center text-[23px] font-normal tracking-[-0.01em] text-[#0d0d0d]">
Where should we begin?
</h1>
 
<div
data-agent-id="compose-card"
className="mt-5 flex w-full max-w-[430px] items-center gap-1 rounded-full border border-black/10 bg-white py-2 pl-3.5 pr-2 shadow-[0_2px_10px_rgba(0,0,0,0.06)] transition-shadow focus-within:shadow-[0_2px_14px_rgba(0,0,0,0.1)]"
>
<Plus className="h-[18px] w-[18px] shrink-0 text-[#0d0d0d]" aria-hidden="true" />
<ComposeTextarea
aria-label="Chat with ChatGPT"
placeholder="Ask ChatGPT"
rows={1}
className="min-w-0 flex-1 px-2 py-1 text-[14.5px] leading-5 text-[#0d0d0d] placeholder:text-black/45"
/>
<button
type="button"
aria-label="Start dictation"
className="grid h-8 w-8 shrink-0 place-items-center rounded-full text-[#0d0d0d] transition-colors hover:bg-black/5"
>
<Mic className="h-[17px] w-[17px]" aria-hidden="true" />
</button>
<ComposeSendButton
aria-label="Send message"
className="h-8 w-8 shrink-0 bg-[#a8c6f8] text-white hover:bg-[#8fb6f6] disabled:bg-[#a8c6f8] disabled:text-white disabled:opacity-100"
icon={<ArrowUp className="h-[17px] w-[17px]" strokeWidth={2.4} />}
/>
</div>
 
<button
type="button"
onClick={() => send("What can you do?")}
className="mt-3 rounded-full border border-black/10 bg-white px-4 py-1.5 text-[12px] text-[#0d0d0d] shadow-[0_1px_2px_rgba(0,0,0,0.04)] transition-colors hover:bg-[#f7f7f7]"
>
What can you do?
</button>
 
<div className="absolute inset-x-0 bottom-2">
<ComposeDisclaimer className="mt-0 text-[9.5px] text-black/45">
ChatGPT is AI. By using it, you agree to our{" "}
<a href="#" className="underline">
Terms
</a>{" "}
&amp;{" "}
<a href="#" className="underline">
Privacy Policy
</a>
.
</ComposeDisclaimer>
</div>
</div>
</div>
);
}
 
export function OpenAICompose(props: ComposeProps) {
return (
<Root {...props}>
<OpenAIComposeLayout />
</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.

ChatGPT also has a full product page.

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

Open ChatGPT
Use it with the rest of the library

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

166 lines · copy-ready