ToolFallback
Overview
The ToolFallback component displays a default ToolUI for tools that do not have a dedicated ToolUI.
Getting Started
Add tool-fallback
npx shadcn@latest add @assistant-ui/tool-fallbackMain Component
npm install @assistant-ui/reactyarn add @assistant-ui/reactpnpm add @assistant-ui/reactbun add @assistant-ui/reactxpm add @assistant-ui/reactimport type { ToolCallMessagePartComponent } from "@assistant-ui/react";import { CheckIcon, ChevronDownIcon, ChevronUpIcon, XCircleIcon,} from "lucide-react";import { useState } from "react";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";export const ToolFallback: ToolCallMessagePartComponent = ({ toolName, argsText, result, status,}) => { const [isCollapsed, setIsCollapsed] = useState(true); const isCancelled = status?.type === "incomplete" && status.reason === "cancelled"; const cancelledReason = isCancelled && status.error ? typeof status.error === "string" ? status.error : JSON.stringify(status.error) : null; return ( <div className={cn( "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3", isCancelled && "border-muted-foreground/30 bg-muted/30", )} > <div className="aui-tool-fallback-header flex items-center gap-2 px-4"> {isCancelled ? ( <XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" /> ) : ( <CheckIcon className="aui-tool-fallback-icon size-4" /> )} <p className={cn( "aui-tool-fallback-title grow", isCancelled && "text-muted-foreground line-through", )} > {isCancelled ? "Cancelled tool: " : "Used tool: "} <b>{toolName}</b> </p> <Button onClick={() => setIsCollapsed(!isCollapsed)}> {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />} </Button> </div> {!isCollapsed && ( <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2"> {cancelledReason && ( <div className="aui-tool-fallback-cancelled-root px-4"> <p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground"> Cancelled reason: </p> <p className="aui-tool-fallback-cancelled-reason text-muted-foreground"> {cancelledReason} </p> </div> )} <div className={cn( "aui-tool-fallback-args-root px-4", isCancelled && "opacity-60", )} > <pre className="aui-tool-fallback-args-value whitespace-pre-wrap"> {argsText} </pre> </div> {!isCancelled && result !== undefined && ( <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2"> <p className="aui-tool-fallback-result-header font-semibold"> Result: </p> <pre className="aui-tool-fallback-result-content whitespace-pre-wrap"> {typeof result === "string" ? result : JSON.stringify(result, null, 2)} </pre> </div> )} </div> )} </div> );};shadcn/ui dependencies
npm install @radix-ui/react-slotyarn add @radix-ui/react-slotpnpm add @radix-ui/react-slotbun add @radix-ui/react-slotxpm add @radix-ui/react-slotimport * as React from "react";import { Slot } from "@radix-ui/react-slot";import { cva, type VariantProps } from "class-variance-authority";import { cn } from "@/lib/utils";const buttonVariants = cva( "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm no-underline outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9", }, }, defaultVariants: { variant: "default", size: "default", }, },);function Button({ className, variant, size, asChild = false, ...props}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & { asChild?: boolean; }) { const Comp = asChild ? Slot : "button"; return ( <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} /> );}export { Button, buttonVariants };This adds a /components/assistant-ui/tool-fallback.tsx file to your project, which you can adjust as needed.
Use it in your application
Pass the ToolFallback component to the MessagePrimitive.Parts component
import { } from "@/components/assistant-ui/tool-fallback";
const : = () => {
return (
<. ="...">
< ="...">
<.
={{ : { : } }}
/>
</>
< />
< ="..." />
</.>
);
};