Tool UI

DocsGalleryassistant-ui
GitHub RepositoryX (Twitter)
Gallery
Get Started
Overview
Quick Start
Response Actions
Receipts
Advanced
UI Guidelines
Contributing
Progress
Plan
Progress Tracker
Input
Option List
Parameter Slider
Preferences Panel
Question Flow
Display
Citation
Link Preview
Item Carousel
Stats Display
Terminal
Weather Widget
Artifacts
Chart
Code Block
Data Table
Message Draft
Social Posts
Confirmation
Approval Card
Order Summary
Media
Image
Image Gallery
Video
Audio
Link PreviewOption List

Message Draft

Review and approve messages before an AI agent sends them.

Q4 Planning Follow-up

Tosarah.chen@company.com

Hi Sarah, Thanks for joining the planning meeting today. I've attached the updated timeline. Best, Alex

import { MessageDraft } from "@/components/tool-ui/message-draft";export function Example() {  return (    <MessageDraft      id="message-draft-example"      channel="email"      subject="Q4 Planning Follow-up"      to={["sarah.chen@company.com"]}      body={`Hi Sarah,Thanks for joining the planning meeting today. I've attached the updated timeline.Best,Alex`}      onSend={() => console.log("Sent")}      onCancel={() => console.log("Cancelled")}    />  );}

Key Features

Human-in-the-loop

Review messages before the AI sends them on your behalf

Undo grace period

Cancel within a configurable window after clicking Send

Channel skins

Email and Slack with channel-specific metadata display

Receipt states

Compact confirmation after send or cancel

Channel Skins

The component supports multiple channel types, each with its own metadata fields.

Email

Shows subject line, To/CC/BCC recipients, and message body.

Re: Q4 Budget Review

Tofinance@company.com
Ccmanager@company.com

Hi team, I've compiled the department requests. Key changes: - Engineering: +15% for infrastructure - Marketing: Flat (reallocating to digital) Please review by Friday. Thanks, Dana

<MessageDraft  id="email-example"  channel="email"  subject="Re: Q4 Budget Review"  to={["finance@company.com"]}  cc={["manager@company.com"]}  body="..."  onSend={() => sendEmail()}  onCancel={() => console.log("Cancelled")}/>

Slack

Shows channel or DM target with the Slack icon.

#eng-releases

Shipped v2.4.1 to production: - Fixed auth token refresh bug - Improved search latency by 40% Monitoring dashboards look good.

Message to @Alex Rivera

Hey Alex, just wanted to check in on the API integration. Are we still on track for the Thursday demo?

Send Flow

After clicking Send, there's a grace period where the user can undo:

  1. Review - User reviews the draft with Send/Cancel buttons
  2. Sending - Countdown with Undo button (default 5 seconds)
  3. Sent - Compact receipt confirming the message was sent

The grace period is configurable via undoGracePeriod (in milliseconds).

Receipt States

When outcome is set, the component renders a compact receipt showing the result. This is useful for displaying the outcome in conversation history.

Interview follow-up

Tohiring@company.com

Thank you for the interview...

Sent at 11:18 PM

Source and Install

Copy components/tool-ui/message-draft and the shared directory into your project. The shared folder contains utilities used by all Tool UI components. The tool-ui directory should sit alongside your shadcn ui directory.

Prerequisites

This component requires the following shadcn/ui components:

pnpm dlx shadcn@latest add button separator

Directory Structure

action-buttons.tsx
schema.ts
parse.ts
index.ts
...
message-draft.tsx
schema.ts
index.tsx
error-boundary.tsx
_adapter.tsx

Download

  • Shared Dependencies (GitHub) (ZIP)
  • Message Draft (GitHub) (ZIP)

Usage

"use client";import { makeAssistantTool } from "@assistant-ui/react";import {  MessageDraft,  MessageDraftErrorBoundary,  parseSerializableMessageDraft,  SerializableMessageDraftSchema,  type SerializableMessageDraft,  type MessageDraftOutcome,} from "@/components/tool-ui/message-draft";export const MessageDraftTool = makeAssistantTool<  SerializableMessageDraft,  MessageDraftOutcome>({  toolName: "draftMessage",  description: "Draft a message for the user to review before sending.",  parameters: SerializableMessageDraftSchema,  render: ({ args, result, addResult, toolCallId }) => {    // Wait for channel to determine which fields are required    if (!(args as any)?.channel || !(args as any)?.body) return null;    const draft = parseSerializableMessageDraft({      ...args,      id: (args as any)?.id ?? `message-draft-${toolCallId}`,    });    return (      <MessageDraftErrorBoundary>        {result !== undefined ? (          <MessageDraft {...draft} outcome={result} />        ) : (          <MessageDraft            {...draft}            onSend={async () => {              // Perform actual send logic here              await sendMessage(draft);              addResult("sent");            }}            onCancel={() => addResult("cancelled")}          />        )}      </MessageDraftErrorBoundary>    );  },});

Mount <MessageDraftTool /> under <AssistantRuntimeProvider> to register the tool and its UI.

Props

Prop

Type

Email-specific Props

Prop

Type

Slack-specific Props

Prop

Type

Accessibility

  • Uses article with aria-labelledby for the draft content
  • Keyboard navigation:
    • Tab to move between buttons
    • Enter or Space to activate focused button
    • Escape triggers cancel while in review state
  • Sending state announces countdown via aria-live="polite"
  • Receipt state uses role="status" for screen reader announcements
  • Focus moves to Undo button when entering sending state