Confirmation
Human-in-the-loop approval prompt for tool calls, with request, approved and rejected states driven by tool state.
Overview
Confirmation blocks a tool call until the user approves or rejects it. It wraps an Alert and exposes three slot components (ConfirmationRequest, ConfirmationAccepted, ConfirmationRejected) that auto-show or hide based on the AI SDK ToolUIPart state and the approval verdict.
Use it for irreversible operations (deletes, payments, deploys, write-to-prod) where the user needs to confirm before the tool executes.
Preview
Requesting approval
Approved
Rejected
Installation
bash npx gremorie@latest add rx-confirmation bash pnpm dlx gremorie@latest add rx-confirmation
bash yarn dlx gremorie@latest add rx-confirmation
bash bunx --bun gremorie@latest add rx-confirmation
Usage
import {
Confirmation,
ConfirmationTitle,
ConfirmationRequest,
ConfirmationAccepted,
ConfirmationRejected,
ConfirmationActions,
ConfirmationAction,
} from "@gremorie/rx-ai";
export function Example({ state, approval, approve, reject }) {
return (
<Confirmation state={state} approval={approval}>
<ConfirmationTitle>Run delete_records on production?</ConfirmationTitle>
<ConfirmationRequest>
<ConfirmationActions>
<ConfirmationAction variant="outline" onClick={reject}>
Reject
</ConfirmationAction>
<ConfirmationAction onClick={approve}>Approve</ConfirmationAction>
</ConfirmationActions>
</ConfirmationRequest>
<ConfirmationAccepted>Tool executed.</ConfirmationAccepted>
<ConfirmationRejected>Tool blocked.</ConfirmationRejected>
</Confirmation>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
API
<Confirmation>
| Prop | Type | Default | Description |
|---|---|---|---|
state | ToolUIPart["state"] | - | Required tool call state from the AI SDK. The component returns null while state is input-streaming or input-available. |
approval | { id: string; approved?: boolean; reason?: string } | - | Required approval payload. Drives which slot is rendered. |
Extends ComponentProps<typeof Alert>.
<ConfirmationTitle>
Renders an inline AlertDescription. Use it for the prompt copy.
<ConfirmationRequest> / <ConfirmationAccepted> / <ConfirmationRejected>
| Component | Renders when |
|---|---|
ConfirmationRequest | state === "approval-requested" |
ConfirmationAccepted | approval.approved === true and state is approval-responded, output-denied or output-available |
ConfirmationRejected | approval.approved === false and state is approval-responded, output-denied or output-available |
Each accepts children: ReactNode.
<ConfirmationActions> / <ConfirmationAction>
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Action buttons. The container only renders during approval-requested. |
ConfirmationAction extends Button props with a smaller default size (h-8 px-3 text-sm).
useConfirmation()
Hook that returns { approval, state }. Throws when used outside <Confirmation>. Useful for custom slots.
Composition
<Confirmation>wraps anAlertand provides the state context.<ConfirmationTitle>is the headline question.<ConfirmationRequest>shows only while approval is pending. Place actions inside.<ConfirmationActions>+<ConfirmationAction>are the Approve / Reject buttons.<ConfirmationAccepted>/<ConfirmationRejected>show the resolved outcome.
Variations
Three-state alert
Render every slot so the same JSX serves the full lifecycle.
<Confirmation state={state} approval={approval}>
<ConfirmationTitle>Charge $499 to the saved card?</ConfirmationTitle>
<ConfirmationRequest>
<ConfirmationActions>
<ConfirmationAction variant="outline" onClick={reject}>
Cancel
</ConfirmationAction>
<ConfirmationAction onClick={approve}>Charge</ConfirmationAction>
</ConfirmationActions>
</ConfirmationRequest>
<ConfirmationAccepted>Card charged successfully.</ConfirmationAccepted>
<ConfirmationRejected>Charge cancelled.</ConfirmationRejected>
</Confirmation>Destructive variant
Use Alert variants and a destructive action button to signal severity.
<Confirmation state={state} approval={approval} variant="destructive">
<ConfirmationTitle>Permanently delete 1,204 records?</ConfirmationTitle>
<ConfirmationRequest>
<ConfirmationActions>
<ConfirmationAction variant="outline" onClick={reject}>
Keep them
</ConfirmationAction>
<ConfirmationAction variant="destructive" onClick={approve}>
Delete
</ConfirmationAction>
</ConfirmationActions>
</ConfirmationRequest>
</Confirmation>Approval with reason
Surface the rejection reason captured on approval.reason.
<Confirmation state={state} approval={approval}>
<ConfirmationTitle>Deploy to production?</ConfirmationTitle>
<ConfirmationRejected>
<p>Deploy blocked. Reason: {approval?.reason ?? 'no reason given'}.</p>
</ConfirmationRejected>
</Confirmation>Accessibility
- Keyboard: action buttons participate in tab order and respond to Enter / Space.
- ARIA: the underlying
Alertexposesrole="alert"so screen readers announce the prompt when it appears. - Screen readers: each slot uses semantic text, so the request, accepted and rejected copy read sequentially as the state transitions.
- Focus management: when the prompt mounts, move focus programmatically to the primary action so keyboard users can confirm without hunting for it.