81 / 122 · Composer
Draft restore
Come back to a thread and the sentence you never sent is still waiting.
Installation
npx shadcn@latest add "@assistant-ui/elements-draft-restore"
Usage
import { DraftRestore } from "@/components/elements/draft-restore";
<DraftRestore
draft="Add a regression test for draft restore"
savedAt="2 minutes ago"
onRestore={restore}
onDiscard={discard}
/>Props
draft*stringThe unsent text, truncated to one line.
savedAt*stringWhen it was last touched, pre-formatted.
onRestore() => voidCalled to put the draft back in the composer.
onDiscard() => voidCalled to throw the draft away.
classNamestringExtra classes merged onto the root.
Source
draft-restore.tsx"use client";
import { PencilLineIcon, XIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { ghostButton, mono, paper } from "./surfaces";
export function DraftRestore({
draft,
savedAt,
onRestore,
onDiscard,
className,
}: {
draft: string;
savedAt: string;
onRestore?: () => void;
onDiscard?: () => void;
className?: string;
}) {
return (
<div
className={cn(
paper,
"fade-in slide-in-from-bottom-1 animate-in flex w-full max-w-sm items-center gap-2.5 rounded-2xl py-2 pr-2 pl-3.5 duration-300",
className,
)}
>
<PencilLineIcon className="text-foreground/30 size-3.5 shrink-0" />
<div className="flex min-w-0 flex-1 flex-col">
<span className="text-foreground/70 truncate text-[13px]">{draft}</span>
<span className={cn(mono, "text-foreground/30")}>
unsent draft · {savedAt}
</span>
</div>
<button
type="button"
onClick={onRestore}
className="text-foreground/70 hover:bg-foreground/[0.06] hover:text-foreground/95 shrink-0 rounded-full px-2.5 py-1 text-xs font-medium transition-[background-color,color,scale] duration-150 active:scale-[0.96]"
>
Restore
</button>
<button
type="button"
aria-label="Discard the draft"
onClick={onDiscard}
className={cn(ghostButton, "size-7 shrink-0")}
>
<XIcon className="size-3.5" />
</button>
</div>
);
}