Arc
ComponentsBlocksDocsPricingTry the demo
Introduction
Installation
Theming
Motion
AI and MCP
Changelog
Metrics dashboard
KPI drilldown
Metric explorer
Revenue explorer
MRR waterfall
Cohort retention
Usage forecast
Journey flow
Choropleth explorer
Connected scatter
Market terminal
Budget variance
Small multiples
Line replay
AI chat
AI composer
AI answerNew
Model pickerNew
Model arenaNew
Prompt studioNew
Document extractionNew
Agent memoryNew
AI side panel
Agent run
Agent tasksNew
Agent approvalsNew
Agent routinesNew
Agent orchestratorNew
MCP serversNew
Knowledge baseNew
Eval suiteNew
SidebarPro4
Page header
Command palette
Notification center
Selection toolbarNewPro
Empty states
Search resultsPro
Login and sign up5
Onboarding illustrationNewPro
OTP input
Multi-step formPro
Settings page
Settings command
Security settings
Billing overview
Usage billing
Cancel flow
Team members
Invite people
Team directory
Roles and permissions
API keys
Webhooks
Integrations
Environment variables
Plan comparison
Pricing calculatorPro
Usage pricingPro
Cart drawerPro
Checkout summaryPro
Live checkoutNewPro
Invoice studio
Wallet card
Product listing
Product detail
Inbox triage
Support conversation
Support widget
Help center
File upload
Media playerPro
Week calendar
Availability picker
Project board
Release readiness
Kanban board
Hero section
Hero signupPro
Scroll storyPro
Spotlight gridPro
Feature bentoPro
Comparison table
Logo marquee
Studio perspectivesPro
Stats band
Changelog feed
FAQ section
Contact section
Team showcasePro
Blog grid
Site header
Site footer
CTA section
Newsletter signup
Activity terrain
Revenue globe
Customer galaxy
Semantic zoom
Layout morph
Exploded viewNew
What’s new31 new
BlocksPrompt studio

Prompt studio

Pro

One card for editing, testing and shipping a prompt template: variables as chips with sample values, a test run against the chosen model, and versions you can compare and promote to production.

Live block · sample data

When to use

  • A settings or admin page where a support lead or engineer tunes the prompt behind one AI feature and tests it on real cases.
  • Prompt management that needs a compact version history with authors, a diff against an earlier version and a visible production pointer.
  • Any LLM feature with template variables filled from records, such as a support reply, a summary or a classifier prompt.

When not to use

  • Use ai-chat for an end user conversation; this card edits templates, not messages.
  • Use model-arena to compare the replies of several models side by side on one prompt.
  • Use eval-suite to score a prompt across many cases with graders and pass rates.
  • Use model-picker when the only job is choosing a model and effort.

Installation

Pro source and install commands unlock with a Pro plan.

Usage

Pass template, variables and versions to load your own prompt, run to call your model, and onPromote to ship a version; with no props it edits a sample support prompt.

example.tsx
1import { PromptStudio } from "@/components/arc/blocks/prompt-studio/prompt-studio";2 3export function SupportPrompt({ prompt, cases }) {4  return (5    <PromptStudio6      name={prompt.name}7      versions={prompt.versions}8      productionId={prompt.productionId}9      variables={["customer_name", "order_id", "policy"]}10      testCases={cases}11      onRun={async function* (request, signal) {12        const response = await fetch("/api/prompt-run", { method: "POST", body: JSON.stringify(request), signal });13        const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader();14        for (;;) {15          const { value, done } = await reader.read();16          if (done) return;17          yield value;18        }19      }}20      onPromote={versionId => setProduction(prompt.id, versionId)}21    />22  );23}24 25// A version is plain text with {{variables}}.26const version = {27  id: "v4", label: "v4", createdAt: "2026-10-16T15:20:00Z", message: "Escalate when the policy falls short",28  author: { name: "Hannah Walsh", avatar: "/media/people/hannah-walsh.jpg" },29  system: "You are a support agent for Fernwood. Follow this policy exactly: {{policy}}",30  user: "Reply to the latest message from {{customer_name}} about order {{order_id}}.",31};
Built fromAvatarButtonCopy buttonKbdPopover

API reference

2 parts. The first is the root.

PromptStudio

One card for editing, testing and shipping a prompt template. The header holds the prompt's name, a version chip (a menu of versions with compare, promote to production and undo) and a model chip (model and effort). System and User tabs show the template with {{variable}} placeholders as atomic chips, each variable in its own series color. A row of colored fields fills the variables from saved test cases, and Run streams the reply into a quiet panel at the foot with latency, tokens and cost. Compare turns the editor into an inline word diff.

PropTypeDefaultDescription
namestring"Support reply"Name of the prompt, shown in the header.
versionsPromptVersion[]sampleVersionsSaved versions, newest first. Each has id, label ("v4"), message, author ({ name, avatar? }), createdAt (ISO date) and the system and user text.
versionIdstring–The version open in the editor; the chip shows its label. Defaults to the newest version.
productionIdstring | null–The version production serves; it carries the Production marker. Defaults to the open version.
template{ system: string; user: string }–The draft the editor opens with. Defaults to the open version's text.
variablesstring[]–Variable order for colors and fields: the first four take --series-1 to --series-4, then the colors repeat. Variables found in the text but not listed are added after. Defaults to the order they appear in the open version.
testCasesPromptTestCase[]sampleCasesSaved test cases as { id, label, values }, where values maps a variable name to its value. The first case is loaded on mount. Keep labels short: the cases share one segmented control.
modelsPromptModel[]studioModelsModels for the model menu: id, name, short (chip label), mark ("claude" | "openai" | "gemini"), input and output price per million tokens, sample (marks illustrative prices) and defaultEffort.
defaultModelstring"claude-sonnet-5-5"Id of the model selected on first render.
defaultEffort"low" | "medium" | "high" | "xhigh" | "max"–Effort selected on first render. Defaults to the model's own default (high for Claude Sonnet 5.5). Choosing another model resets effort to that model's default.
nowstring | number | Date–Reference moment for the relative dates in the version menu. Defaults to the current time, or to Oct 20, 2026 for the sample versions so the demo never ages.
onRun(request: PromptRunRequest, signal: AbortSignal) => Promise<string | AsyncIterable<string>> | AsyncIterable<string>–Runs the prompt. The request carries the raw template, the rendered template, the values, model, effort, test case id and version id. Return the whole reply or an async iterable of text deltas; throw to show the error state with Retry. Stop aborts the signal. Without it, runs are simulated and labelled as such.
onPromote(versionId: string, previousId: string | null) => void–Called when a version is promoted to production, and again with the ids swapped when the promotion is undone.
onTemplateChange(template: { system: string; user: string }) => void–Called with the draft after every edit, for autosave.
classNamestring–Class on the root element.

PromptEditor

The chip editor on its own, exported from prompt-editor.tsx. A plain text contenteditable where the string is the model: every edit is applied to the string and the DOM is rebuilt from it, so chips cannot be split and copy and paste keep the braces.

PropTypeDefaultDescription
valueRequiredstring–The template text, with placeholders written as {{name}}.
onChangeRequired(value: string) => void–Called with the new text after every edit. Placeholders are normalized to {{name}} without inner spaces.
variablesRequiredstring[]–Names offered after typing {{.
toneOfRequired(name: string) => number–Color slot (1 to 4) for a variable, read as --series-1 to --series-4.
labelRequiredstring–Accessible name of the text box.
missingstring[]–Variables without a value; their chips draw a dashed outline.
activeVariablestring | null–A variable to light up, for example while its value field has focus.
flash{ name: string; at: number } | null–Flashes every chip of a variable once. Pass a new `at` to flash again, for example on each keystroke in its field.
onChipClick(name: string) => void–Called when a chip is clicked.
onSubmit() => void–Called on Mod+Enter.
placeholderstring–Shown while the text is empty.

Keyboard interactions

Mod+Enter
Runs the prompt for the selected test case from anywhere in the card, including the editors and the variable fields.
{{
In the editor, opens the list of known variables. Arrow up and down move, Enter or Tab inserts the variable as a chip, Escape closes the list. Typing a new name then }} creates a variable and a field for it.
Backspace, Delete
Remove a chip whole when the caret is beside it. Option or Ctrl with Backspace removes a word.
Mod+Z, Shift+Mod+Z
Undo and redo inside the editor. Typing within a short pause is one step.
Arrow leftorArrow right
On the System and User tabs, the test case control and the effort control, move the selection.
Arrow uporArrow down
In the version menu and the model menu, move between rows and select them.
Escape
Closes an open menu. In compare mode, returns to editing.
Tab
Moves through the version chip, model chip, tabs, editor, each variable field, the test cases, Run, and Copy or Retry.

Accessibility

  • The editor is a role="textbox" with aria-multiline and an accessible name per tab; while the {{ list is open it points at the list with aria-controls and at the highlighted option with aria-activedescendant.
  • System and User are real tabs with tabpanels. The hidden panel is inert, so it leaves the tab order and the reading order.
  • Every variable field has a visible label in its variable's color, paired with a colored dot. Clicking a chip focuses its field.
  • Status is never color alone: the version chip says Production or Latest, the run status pairs an icon with Reply, Stopped or Run failed, and an empty variable's chips switch to a dashed outline.
  • The diff uses ins and del elements with visually hidden Added and Removed prefixes, plus a visible + or minus sign before each change and a strikethrough on removals.
  • Version rows and model rows are radios in a labelled group. The Production marker is text, and the menu's status line says which version production serves.
  • Runs, results, errors, promotions, undos and compare mode are announced through one polite status region, including when a run is simulated.
  • Pointer focus draws no ring; keyboard focus shows Arc's shared focus ring, inset on menu rows and segmented options.

Motion

  • Promoting a version glides the Production marker from its old row to the new one on the critically damped smooth spring, with no overshoot. Undo glides it back.
  • The reply streams in chunks of three to six whole words every 40ms on a requestAnimationFrame clock, about 100 words a second, and a backlog widens the chunks. Each chunk only fades in over 150ms; settled text never moves. Thinking lasts 0.5 to 1.2 seconds by effort.
  • The output panel follows its content's height on the smooth spring. A rerun holds the panel at its current height until the new reply is in, then settles, so nothing collapses and regrows.
  • Labels that change (version state, model name, effort, run status, the footer) use one crossfade: the old label fades out in 100ms while the new one fades in from 2px over 160ms, and the chip width follows on a fast spring without bounce. No blur, no per-letter motion.
  • Tab and test case highlights slide on a critically damped spring. Token counts roll their digits with tabular numerals in a fixed slot. Editing a value flashes its chips once.
  • The working label uses a calm shimmer that stops on Reply, Stopped or Run failed.
  • Reduced motion removes the glides, the chunk fade, the shimmer and every height animation while keeping each state, label and announcement.

Responsive behavior

  • The card is up to 760px wide and reads well from 320px. It uses container queries, so it adapts to its slot, not the viewport.
  • Below 560px of container width the variable fields fall into two columns, and below 340px into one.
  • Below 480px the test cases and Run each take a full row, and the model chip wraps under the title.
  • System and User share one editor slot sized to the taller text, so switching tabs never changes the card's height.

Performance

  • The editor rebuilds a few dozen DOM nodes per keystroke from the string, well under a millisecond for prompt sized text.
  • The word diff trims the shared start and end before its longest common subsequence pass, so typical edits compare a few dozen tokens.
  • Streaming appends one chunk per tick and never re-renders earlier chunks; the clock stops when the reply is complete, stopped or failed.
  • Only one run is active at a time; starting another cancels the first and aborts its signal.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Choose it when a team edits a prompt template, tries it on saved cases and promotes a version to production, inside a product page or settings screen.
  • Write placeholders as {{name}} in the system and user text. Pass `variables` to fix the color order; values in test cases are matched by name.
  • Wire onRun to stream real replies. Return an async iterable of text deltas, or a promise of the full text, and respect the AbortSignal: Stop aborts it. The footer estimates tokens and cost from the model prices you pass.
  • Keep onPromote as the source of truth: the card updates its own pointer optimistically and calls back, and Undo calls it again with the ids swapped.
  • Model prices outside Anthropic in the sample list are illustrative. Pass your own models with real prices for cost estimates.
  • Without props it renders the full demo: a Fernwood support reply prompt with four versions, v4 in production and three saved cases. Without onRun, the first run of the Past window case returns a simulated overload error so the retry path can be seen.
/components/blocks/prompt-studio/markdown

The full library index for assistants is at /llms.txt.

Related

Model pickerProBlockA model picker in one quiet pill: open it to choose from featured models with a one line note each, set the reasoning effort, or browse every other model by provider, and the pill swaps to your choice in place.Model arenaProBlockA blind side by side model comparison in one card: send one prompt to two models, watch both answers stream at the same pace, vote for the better one, then reveal which model wrote each.Eval suiteProBlockRun an evaluation suite on a new version of an LLM feature against its baseline: results stream in case by case, and regressions and fixes are called out with the cases behind them.AI chatProBlockA full-screen AI chat where answers stream in as markdown with folding reasoning, tool chips and cited sources. People can switch chats and models, edit or retry a message into versions, rate replies and stop a reply mid-stream.
PreviousModel arenaNextDocument extraction
Arc · 101 block pagesllms.txt

On this page

  • Preview
  • When to use
  • When not to use
  • Installation
  • Usage
  • API reference
  • Keyboard
  • Accessibility
  • Motion
  • Responsive behavior
  • Performance
  • Notes for AI
  • Related
View as Markdown

Support reply

112012345678901234567890123456789tokens
Type {{ to add a variableType {{ to add a variable
Run Late order to see the reply here. Runs are simulated.