07 / 37 · Messages
Message branches
Navigate between regenerated versions of the same answer without losing your place.
1 / 3
Installation
npx shadcn@latest add "@assistant-ui/elements-message-branches"
Usage
import { MessageBranches } from "@/components/elements/message-branches";
<MessageBranches
variants={["First answer.", "Second answer."]}
index={0}
onIndexChange={setIndex}
/>Props
variants*readonly string[]Alternate regenerated answers the user can step through.
index*numberWhich variant is currently displayed.
onIndexChange*(index: number) => voidCalled when the previous or next branch control is pressed.
classNamestringExtra classes merged onto the root.
Source
message-branches.tsx"use client";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { ghostButton, mono } from "./surfaces";
export interface MessageBranchesProps {
variants: readonly string[];
index: number;
onIndexChange: (index: number) => void;
className?: string;
}
export function MessageBranches({
variants,
index,
onIndexChange,
className,
}: MessageBranchesProps) {
const message = variants[index] ?? variants[0] ?? "";
const hasNavigation = variants.length > 1;
const goPrevious = () => {
if (!hasNavigation) return;
onIndexChange(index === 0 ? variants.length - 1 : index - 1);
};
const goNext = () => {
if (!hasNavigation) return;
onIndexChange(index === variants.length - 1 ? 0 : index + 1);
};
return (
<div className={cn("flex max-w-sm flex-col gap-2", className)}>
<p
key={index}
className="fade-in slide-in-from-bottom-1 animate-in text-foreground/90 min-h-[4.25rem] text-sm leading-relaxed duration-300 motion-reduce:animate-none"
>
{message}
</p>
<div className="flex items-center gap-1">
<button
type="button"
aria-label="Show previous response"
disabled={!hasNavigation}
onClick={goPrevious}
className={cn(ghostButton, "size-6")}
>
<ChevronLeftIcon className="size-3.5" />
</button>
<span className={cn(mono, "text-foreground/35 tabular-nums")}>
{variants.length === 0
? "0 / 0"
: `${index + 1} / ${variants.length}`}
</span>
<button
type="button"
aria-label="Show next response"
disabled={!hasNavigation}
onClick={goNext}
className={cn(ghostButton, "size-6")}
>
<ChevronRightIcon className="size-3.5" />
</button>
</div>
</div>
);
}