Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new

Confirmation

A confirmation request that presents a tool action and records a clear decision.

Interactive preview

The shared preview uses React Base. The source below uses React Aria. Each example runs with local data.

Installation

Complete project setup with react/aria, then install these components:

pnpm exec frontfriend download item button icon message bubble -f react --primitive aria

The example never runs a command. Each request accepts one decision; Reset creates another request. Keep the request ID when you connect the decision to a tool approval response. Do not run the tool before approval.

Usage

Complete example

import { Message, MessageContent } from "@/components/ui/message";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
import {
  Item,
  ItemMedia,
  ItemContent,
  ItemTitle,
  ItemDescription,
  ItemActions,
} from "@/components/ui/item";
import { Icon } from "@/components/ui/icon";
import { Button } from "@/components/ui/button";
import * as React from "react";
export default function AiConfirmationExample() {
  const [decision, setDecision] = React.useState<
    "pending" | "approved" | "denied"
  >("pending");
  const resultRef = React.useRef<HTMLParagraphElement>(null);
  React.useEffect(() => {
    if (decision !== "pending") resultRef.current?.focus();
  }, [decision]);
  function decide(value: "approved" | "denied") {
    setDecision(value);
  }
  return (
    <div
      style={{
        width: "100%",
        maxWidth: 560,
        minWidth: 0,
        marginInline: "auto",
        display: "flex",
        flexDirection: "column",
        gap: 16,
        paddingBlock: 16,
        fontSize: "var(--text-sm)",
        lineHeight: 1.5,
      }}
    >
      <Message align="end">
        <MessageContent>
          <Bubble align="end" variant="muted">
            <BubbleContent>
              Run the build before I ship this change.
            </BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      {decision === "pending" && (
        <Item variant="outline" size="sm">
          <ItemMedia variant="icon">
            <Icon name="Terminal" />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>Run {"pnpm build"}?</ItemTitle>
            <ItemDescription>The command runs in this project.</ItemDescription>
          </ItemContent>
          <ItemActions>
            <Button
              size="sm"
              variant="outline"
              onPress={() => decide("denied")}
            >
              Deny
            </Button>
            <Button size="sm" onPress={() => decide("approved")}>
              Approve
            </Button>
          </ItemActions>
        </Item>
      )}
      {decision !== "pending" && (
        <Message>
          <MessageContent>
            <p ref={resultRef} tabIndex={-1}>
              {decision === "approved"
                ? `Build approved. Ready to run ${"pnpm build"}.`
                : "Build cancelled. No command was run."}
            </p>
          </MessageContent>
        </Message>
      )}
      <div>
        <Button
          size="sm"
          variant="ghost"
          onPress={() => setDecision("pending")}
        >
          Reset
        </Button>
      </div>
    </div>
  );
}

Components and API

This composition has no separate component API. Use the APIs for its installed components.

ComponentTarget reference
item React Aria API
button React Aria API
icon React Aria API
message React Aria API
bubble React Aria API