Skip to content
Sign in
Apps SDKEmail draftMenu

Email draftPro

A draft email the assistant writes and you finish in the thread, sent with a few seconds to undo.

ProA note to move a review: the draft writes itself in, then you edit it and send with five seconds to undo.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/email-draft

Usage

tsx
import { EmailDraft } from "@/components/ui/chatcn/pro/email-draft"

<EmailDraft
  value={draft}
  onValueChange={setDraft}
  status={streaming ? "drafting" : "ready"}
  from="ines@tavares.pt"
  onSend={(draft) => sendMail(draft)}
/>

The value holds to, cc, subject and body. While status is drafting the body can stream in and every field is read only; the header shows the only shimmer the kit allows. Once it settles, that version is kept as the original: any edit shows Edited in the header with Revert to draft, so the assistant's wording is one click away. Pass original to set it yourself.

Recipients are chips. Enter, comma, Tab or leaving the field turns text into chips, a pasted list splits into several, and Backspace on an empty field takes the last chip back for editing. An address that is not one stays as a red chip with the fix in words, and Send waits until it is fixed or removed.

Send starts an undo window (undoSeconds, default 5): a hairline of accent drains across the footer and Undo takes the place of Send. Then onSend runs; a rejection keeps the draft and says what to do. Open in mail app is a mailto link with every field, or your own handler with onOpenInClient. Render it inside the card your host draws for a widget answer. It fills the width it is given, so a comfortable max width (around 28 rem) comes from the card, and it follows the host theme through the kit tokens. Pass onExpand where the host can go full screen.

Examples

States

ProDrafting, edited with Revert, an address to fix, a long message with Cc and attachments, sent, no recipients and disabled.

Props

PropTypeNotes
value, defaultValue, onValueChange{ to, cc, subject, body }Recipients are { email, name? }
originalEmailDraftValueFor Edited and Revert. Defaults to the settled draft
status"drafting" | "ready" | "sent"Default ready
onSend(draft) => unknown | PromiseRuns after the undo window
undoSecondsnumberDefault 5; 0 sends at once
onOpenInClient(draft) => voidReplaces the mailto link
from, attachments, sentAtattachments are { name, size? }
bodyLinesnumberLines before a long body folds. Default 10
locale, disabled
labelsPartial<…Labels>Every string the widget writes, for translation

Labels

labels covers the header (titleTo(names)), field names and placeholders, chip removal (remove(who)), the address error (invalid(email)), drafting, Edited and Revert, the fold, Send, the undo countdown (sendingIn(seconds)), sent and failed lines and join for name lists.

Keyboard

KeyAction
Enter, comma or Tab in To or CcTurn the text into a chip
Backspace in an empty To or CcTake the last chip back to edit it
TabTo, Cc, subject, body, then Open in mail app and Send
Enter or Space on UndoCancel the send while the hairline drains

Accessibility

  • Every field has a visible label. A bad address sets aria-invalid on its field and the error is linked to it.
  • Chips name the person and, for screen readers, the address; each has a labelled remove button.
  • The undo countdown is a polite live region. Sent is a status message.
  • The IME guard keeps Enter from committing a chip mid composition.

Motion

The drain is a linear scale over the undo window, because it is a timer. The sent line enters with a 220 ms fade from 0.96. Reduced motion keeps the countdown text and drops the drain.