MEMS 2018WelcomeGeneral InfoAuthors / AbstractsSponsors / ExhibitsRegistrationAbout BelfastContact

Recreating the ChatGPT-Style Composer: Anatomy of a Modern AI Prompt Box

Open ChatGPT, Claude, or Perplexity and look, really look, at the box you type into. It seems simple: a rounded field with a button. But that little composer is one of the most carefully tuned pieces of UI in the whole product. It's the first thing a user touches and the last thing before a request goes out, so every detail carries weight. This is an anatomy lesson: what actually makes up a modern AI prompt box, why each part exists, and how to build one without reinventing the hard bits.

Dissecting the modern composer

Strip the ChatGPT input down to its components and a clear pattern emerges. The same handful of elements recur across nearly every serious AI product, because each one solves a real friction point.

The rounded pill

The composer is a soft, rounded container, often a single line that grows as you type. That shape isn't decoration. It signals "this is one focused field, give it your whole attention," and it visually separates the act of composing from the conversation scrolling above. It auto-grows on focus and shrinks back when empty, so it stays out of the way until you need room.

The inline model selector

Tucked into the composer itself is a small control to switch models, say a fast model versus a reasoning model, or Sonnet versus Opus. Putting it inside the box matters: model choice is a per-message decision, so it belongs next to the message, not buried in a settings page. It's a tiny menu doing surprisingly heavy lifting for power users.

Dictation and the voice-to-send swap

Here's the detail most people feel but never consciously notice. When the input is empty, the trailing button is a microphone (dictation) or a voice-mode icon. The moment you type a single character, that same button morphs into a send arrow. One button, two jobs, switched on the presence of text. It's a beautiful piece of state-driven design: the affordance always matches what you can actually do right now. While a response streams, that same slot often becomes a stop button.

Rotating placeholders

An empty composer is a blank-page problem. Rotating placeholder text, cycling through hints like "Ask anything…", "Summarize this article…", and "Write a SQL query…", gently teaches capability and lowers the intimidation of the empty field. It's a small touch with an outsized effect on first-run engagement.

The quiet details underneath

Beyond the visible controls there's a layer most users never name but immediately miss when it's absent. Enter sends and Shift+Enter inserts a newline, the opposite of a document editor's default, and getting it wrong makes a composer feel broken. Pasting a screenshot should attach it as a thumbnail with a loading state and a remove button, not dump a base64 blob into the field. URLs should auto-link as you type. And the whole thing has to stay keyboard-navigable and ARIA-labelled, because a prompt box is a primary control, not a decorative one. None of these are headline features; collectively they're the difference between a composer that feels native and one that feels like a prototype.

The genius of a great AI composer is that it looks effortless. Every "obvious" detail, the morphing button, the inline model menu, the rotating hints, is a deliberate decision someone sweated over.

You don't have to rebuild this from scratch

Reproducing all of that by hand is a real project. The button-swap logic, the auto-grow, the focus states, the model menu, the streaming-aware stop button, the placeholder animation: each is small, but together they're a week you didn't budget for. This is where Prompt Area earns its keep. It's a production-grade input purpose-built for prompt-style composers, and crucially it ships a ready-made Styles page at prompt-area.com/styles: real, copy-paste compositions of these exact patterns, built from the same underlying component plus its Action Bar and Status Bar companions.

The headliner is the ChatGPT style: a single-line rounded pill with an inline model selector, dictation, and the voice-to-send arrow that flips to a send button the instant there's text. It's the anatomy described above, assembled and ready to drop in. And it's not the only one. The Styles page also ships:

  • Claude style: a rounded card with the input sitting over an inline control row, a model menu with descriptions, the coral send button, a dismissible notice, and suggested-prompt chips.
  • Claude Code style: env and repo context above the input, a permission-mode menu, model and reasoning-effort selectors, and a plan-usage meter, built from Prompt Area plus the Action Bar and Status Bar.
  • OpenAI Codex style: a Codex-like composer for agentic coding flows.
  • Perplexity style: a search-and-answer composer in the Perplexity mold.

Each is a genuine composition rather than a screenshot, so you can lift the one closest to your product and adjust from there. The value isn't just the visual styling; it's that the fiddly behaviors are already wired. The morphing send button, the auto-grow, the attachment thumbnails, the inline markdown preview, the chip-preserving copy/paste, and IME handling for CJK input all come along, so you're composing screens rather than re-solving solved problems. Prompt Area itself carries zero extra dependencies, with no ProseMirror, Slate, or Lexical, and installs from npm with a self-contained styles.css (no Tailwind required) or via the shadcn registry so the source lands in your repo and you own it. The surface stays small too: one component, PromptArea, and one hook, usePromptAreaState(), which keeps the whole thing easy to reason about even after you've customized it.

Wiring the swap: text presence drives the button

The voice-to-send swap is worth implementing carefully because it's the interaction users feel most. The rule is simple: derive the button from state, never toggle it imperatively. Combine that with rotating placeholders and an onSubmit that respects Enter-to-send, and you've got the core of the composer.

const PLACEHOLDERS: string[] = [ 'Ask anything…', 'Summarize this article…', 'Write a SQL query…', 'Explain this code…', ] function Composer({ onSubmit }: { onSubmit: (text: string) => void }) { const [text, setText] = useState('') const [hint, setHint] = useState(0) // rotate the placeholder while the field is empty useEffect(() => { if (text.length > 0) return const id = setInterval( () => setHint((i) => (i + 1) % PLACEHOLDERS.length), 3000, ) return () => clearInterval(id) }, [text]) const hasText = text.trim().length > 0 const handleSubmit = () => { if (!hasText) return onSubmit(text.trim()) setText('') } return ( <form onSubmit={(e) => { e.preventDefault(); handleSubmit() }}> <input value={text} placeholder={PLACEHOLDERS[hint]} onChange={(e) => setText(e.target.value)} /> {/* one slot, two affordances, driven purely by state */} <button type={hasText ? 'submit' : 'button'} aria-label={hasText ? 'Send' : 'Dictate'}> {hasText ? <SendArrowIcon /> : <MicIcon />} </button> </form> ) }

With Prompt Area you don't hand-roll this, since the Action Bar handles the morphing button and its states, but the principle is the same: the affordance is a function of the input, never a manual flag you have to keep in sync.

The seam where it meets a model

A composer is only half the story; eventually it has to talk to a model, and the cleanest way to do that is the Vercel AI SDK. The division of labor is tidy: Prompt Area owns the input, the AI SDK's useChat owns the stream, and they meet at essentially one line, where you take the composed value and pass it along on submit.

import { useChat } from '@ai-sdk/react' import { segmentsToPlainText } from 'prompt-area' const { sendMessage, status, stop } = useChat() // on submit, hand the prompt to the stream sendMessage({ text: segmentsToPlainText(segments) })

The SDK's status (ready, submitted, streaming, error) is exactly what drives that action-bar swap: while a response streams, the send button becomes a stop button wired to stop(). And because chips are structured, you can read typed values with getChipsByTrigger() and send the chosen model or any mentions in the request body alongside the prompt, then validate and stream on the server with streamText. It stays provider-agnostic: swap @ai-sdk/anthropic for @ai-sdk/openai or @ai-sdk/google without touching the input.

The takeaway

The modern AI prompt box only looks simple. Behind the rounded pill sits an inline model selector, dictation, a state-driven voice-to-send arrow, rotating placeholders, and streaming-aware controls, a stack of small, deliberate decisions. You can rebuild all of it by hand, or you can start from the ChatGPT, Claude, Claude Code, Codex, and Perplexity styles on Prompt Area's Styles page, wire the input to a model through the Vercel AI SDK's one-line seam, and spend your week on the product instead of the composer.


Copyright 2017, All Rights Reserved, MEMS 2018 and interteq.com