āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/ai-elements/confirmation ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
An alert-based component for managing tool execution approval workflows with request, accept, and reject states.
The Confirmation component provides a flexible system for displaying tool approval requests and their outcomes. Perfect for showing users when AI tools require approval before execution, and displaying the approval status afterward.
npx ai-elements@latest add confirmation
import {
Confirmation,
ConfirmationContent,
ConfirmationRequest,
ConfirmationAccepted,
ConfirmationRejected,
ConfirmationActions,
ConfirmationAction,
} from '@/components/ai-elements/confirmation';
<Confirmation approval={{ id: 'tool-1' }} state="approval-requested">
<ConfirmationContent>
<ConfirmationRequest>
This tool wants to access your file system. Do you approve?
</ConfirmationRequest>
<ConfirmationAccepted>
<CheckIcon className="size-4" />
<span>Approved</span>
</ConfirmationAccepted>
<ConfirmationRejected>
<XIcon className="size-4" />
<span>Rejected</span>
</ConfirmationRejected>
</ConfirmationContent>
<ConfirmationActions>
<ConfirmationAction variant="outline" onClick={handleReject}>
Reject
</ConfirmationAction>
<ConfirmationAction variant="default" onClick={handleApprove}>
Approve
</ConfirmationAction>
</ConfirmationActions>
</Confirmation>
Build a chat UI with tool approval workflow where dangerous tools require user confirmation before execution.
Add the following component to your frontend:
// app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport, type ToolUIPart } from 'ai';
import { useState } from 'react';
import { CheckIcon, XIcon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Confirmation,
ConfirmationContent,
ConfirmationRequest,
ConfirmationAccepted,
ConfirmationRejected,
ConfirmationActions,
ConfirmationAction,
} from '@/components/ai-elements/confirmation';
import { Response } from '@/components/ai-elements/response';
type DeleteFileInput = {
filePath: string;
confirm: boolean;
};
type DeleteFileToolUIPart = ToolUIPart<{
delete_file: {
input: DeleteFileInput;
output: { success: boolean; message: string };
};
}>;
const Example = () => {
// Component implementation...
};
Add the following route to your backend:
// app/api/chat/route.tsx
import { streamText, UIMessage, convertToModelMessages } from 'ai';
import { z } from 'zod';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
messages: convertToModelMessages(messages),
tools: {
delete_file: {
description: "Delete a file from the file system",
parameters: z.object({
filePath: z.string().describe('The path to the file to delete'),
confirm: z
.boolean()
.default(false)
.describe('Confirmation that the user wants to delete the file')
})
}
},
requireApproval: true, // Enable approval workflow
execute: async ({ filePath, confirm }) => {
if (!confirm) {
return {
success: false,
message: 'Deletion not confirmed'
};
}
// Simulate file deletion
await new Promise(resolve => setTimeout(resolve, 1000));
return {
success: true,
message: `File ${filePath} deleted successfully`
};
}
});
return new Response(result.stream());
}
Shows the approval request with action buttons when state is approval-requested.
Shows the accepted status when user approves and state is approval-responded or output-available.
Shows the rejected status when user rejects and state is output-denied.
| Prop | Type | | approval? | ToolUIPart['approval'] | | state? | ToolUIPart['state'] | | className? | string | | ...props? | React.ComponentProps<typeof Alert> |
| Prop | Type | | className? | string | | ...props? | React.ComponentProps<typeof AlertDescription> |
| Prop | Type | | children? | React.ReactNode |
| Prop | Type | | children? | React.ReactNode |
| Prop | Type | | children? | React.ReactNode |
| Prop | Type | | className? | string | | ...props? | React.ComponentProps<"div"> |
| Prop | Type | | ...props? | React.ComponentProps<typeof Button> |
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā