Response Actions

Response Actions are lightweight CTAs for human-in-the-loop decision points.

Examples

The following examples show Response Actions in context. Each represents a moment where the AI has done its work and is waiting for human approval before proceeding.

DataTable: Flagged Expenses

AI detected anomalies in recent expenses. The user must approve or escalate before reimbursement proceeds.

Table data shown as expandable cards. Each card represents one row. Columns: Merchant, Amount, Flag.
Delta Airlines
Amount:$847.00
Flag:Possible duplicate
The Capital Grille
Amount:$312.50
Flag:Over policy limit
Uber
Amount:$45.20
Flag:Missing receipt
import { DataTable } from "@/components/tool-ui/data-table";// AI flagged these expenses as anomalies. User decides before processing.<DataTable  id="flagged-expenses"  rowIdKey="id"  columns={[    { key: "merchant", label: "Merchant", priority: "primary" },    {      key: "amount",      label: "Amount",      align: "right",      format: { kind: "currency", currency: "USD" },    },    {      key: "flag",      label: "Flag",      format: {        kind: "status",        statusMap: {          duplicate: { tone: "warning", label: "Possible duplicate" },          policy: { tone: "danger", label: "Over policy limit" },          missing: { tone: "info", label: "Missing receipt" },        },      },    },  ]}  data={[    { id: "1", merchant: "Delta Airlines", amount: 847.00, flag: "duplicate" },    { id: "2", merchant: "The Capital Grille", amount: 312.50, flag: "policy" },    { id: "3", merchant: "Uber", amount: 45.20, flag: "missing" },  ]}  responseActions={{    items: [      { id: "reject", label: "Reject All", variant: "destructive", confirmLabel: "Confirm" },      { id: "approve", label: "Approve All", variant: "default", confirmLabel: "Confirm" },    ],    align: "right",  }}  onResponseAction={(id) => {    if (id === "approve") submitExpenses();    if (id === "reject") returnToSubmitter();  }}/>

Image: Generated Hero Image

AI generated a hero image for the blog post. The user must approve before it goes live.

Abstract visualization of neural networks with glowing nodes
Generated: AI Infrastructure
Generated: AI Infrastructure
import { Image } from "@/components/tool-ui/image";// AI generated this image. User approves before publishing.<Image  id="generated-hero"  assetId="gen-img-7f3a"  src="https://images.unsplash.com/photo-1620712943543-bcc4688e7485"  alt="Abstract visualization of neural networks with glowing nodes"  title="Generated: AI Infrastructure"  description="Style: technical illustration. Prompt: 'neural network visualization...'"  ratio="16:9"  maxWidth="420px"  responseActions={[    { id: "regenerate", label: "Try Again", variant: "secondary" },    { id: "edit", label: "Edit Prompt", variant: "outline" },    { id: "use", label: "Use This", variant: "default" },  ]}  onResponseAction={(id) => {    if (id === "use") attachToPost(assetId);    if (id === "regenerate") generateNewImage();    if (id === "edit") openPromptEditor();  }}/>

XPost: Draft Awaiting Publish

AI drafted a post based on the user's notes. The user reviews and decides whether to publish, schedule, or revise.

Acme Dev avatar
Acme Dev@acme_dev

We just hit 10,000 stars on GitHub! Huge thanks to everyone who contributed, filed issues, and spread the word. Open source is a team sport.

import { XPost } from "@/components/tool-ui/x-post";// AI drafted this post. User approves before it goes live.<XPost  post={{    id: "draft-post-1",    author: {      name: "Acme Dev",      handle: "acme_dev",      avatarUrl: "https://api.dicebear.com/7.x/shapes/svg?seed=acme",      verified: true,    },    text: "We just hit 10,000 stars on GitHub! Huge thanks to everyone...",  }}  responseActions={[    { id: "edit", label: "Edit", variant: "secondary" },    { id: "schedule", label: "Schedule", variant: "outline" },    { id: "post", label: "Post Now", variant: "default", confirmLabel: "Confirm" },  ]}  onResponseAction={(id) => {    if (id === "post") publishImmediately();    if (id === "schedule") openScheduler();    if (id === "edit") openEditor();  }}/>

Action Shape

responseActions accepts either an array of actions or an object with layout config:

// Array formresponseActions={[  { id: "approve", label: "Approve", variant: "default" },  { id: "reject", label: "Reject", variant: "destructive", confirmLabel: "Confirm" },]}// Config form with alignmentresponseActions={{  items: [    { id: "export", label: "Export", variant: "secondary" },    { id: "sync", label: "Sync", variant: "default", confirmLabel: "Confirm" },  ],  align: "right",      // default "right"  confirmTimeout: 3000 // default 3000ms}}

Action Fields

Prop

Type

Handlers

Prop

Type

Serialization

Use SerializableAction when actions come from tool/LLM output. Runtime code can add React icons and handlers on top.

// Tool output (serializable)const toolOutput = {  responseActions: [    { id: "open", label: "Open", variant: "default" },    { id: "save", label: "Save", variant: "secondary" },  ],};// Runtime rendering (can add icons, handlers)<LinkPreview  {...toolOutput}  onResponseAction={(id) => {    if (id === "open") window.open(url, "_blank");    if (id === "save") saveToLibrary(url);  }}/>;

Positioning

Actions render in array order, left to right. What you specify is what you get.

// Visual order: Cancel | Preview | DeployresponseActions={[  { id: "cancel", label: "Cancel", variant: "ghost" },  { id: "preview", label: "Preview", variant: "secondary" },  { id: "deploy", label: "Deploy", variant: "default" },]}

Tab order matches visual order, so keyboard users navigate left-to-right as expected.

Design Guidance

  • Default to 2–3 CTAs tied directly to the content above; avoid toolbar sprawl
  • Use confirmLabel for destructive or high-impact actions
  • Keep confirmTimeout short (default 3000ms) to avoid stale confirm states
  • ActionButtons are responsive by default and stack on narrow containers
  • For richer controls, prefer dedicated components (Option List, filters) and keep this strip focused on decisions
Response Actions | Tool UI