Toolbar
A container for grouping a set of buttons and controls. Built with Base UI and Tailwind CSS. Copy-paste ready.
"use client";
import {
AlignCenterIcon,
AlignLeftIcon,
AlignRightIcon,
DollarSignIcon,
PercentIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
ToggleGroup,
ToggleGroupItem,
} from "@/components/ui/toggle-group";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipPopup,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const items = [
{ label: "Helvetica", value: "helvetica" },
{ label: "Arial", value: "arial" },
{ label: "Times New Roman", value: "times-new-roman" },
];
export default function Particle() {
return (
<TooltipProvider>
<Toolbar>
<ToggleGroup className="border-none p-0" defaultValue={["left"]}>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Align left"
render={<ToggleGroupItem value="left" />}
>
<AlignLeftIcon />
</ToolbarButton>
}
/>
<TooltipPopup sideOffset={8}>Align left</TooltipPopup>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Align center"
render={
<ToggleGroupItem
aria-label="Toggle center"
value="center"
/>
}
>
<AlignCenterIcon />
</ToolbarButton>
}
/>
<TooltipPopup sideOffset={8}>Align center</TooltipPopup>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Align right"
render={
<ToggleGroupItem aria-label="Toggle right" value="right" />
}
>
<AlignRightIcon />
</ToolbarButton>
}
/>
<TooltipPopup sideOffset={8}>Align right</TooltipPopup>
</Tooltip>
</ToggleGroup>
<ToolbarSeparator />
<ToolbarGroup>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Format as currency"
render={<Button size="icon" variant="ghost" />}
>
<DollarSignIcon />
</ToolbarButton>
}
/>
<TooltipPopup sideOffset={8}>Format as currency</TooltipPopup>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Format as percent"
render={<Button size="icon" variant="ghost" />}
>
<PercentIcon />
</ToolbarButton>
}
/>
<TooltipPopup sideOffset={8}>Format as percent</TooltipPopup>
</Tooltip>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<Select defaultValue="helvetica" items={items}>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
render={
<SelectTrigger>
<SelectValue />
</SelectTrigger>
}
/>
}
/>
<TooltipPopup sideOffset={8}>
Select a different font
</TooltipPopup>
</Tooltip>
<SelectPopup>
{items.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectPopup>
</Select>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<ToolbarButton render={<Button />}>Save</ToolbarButton>
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}
Installation
pnpm dlx shadcn@latest add @cnippet/toolbar
Usage
import { Button } from "@/registry/default/ui/button"
import { Toggle } from "@/registry/default/ui/toggle"
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/registry/default/ui/toolbar"<Toolbar>
<ToolbarGroup>
<ToolbarButton render={<Toggle />}>Bold</ToolbarButton>
<ToolbarButton render={<Toggle />}>Underline</ToolbarButton>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarButton render={<Button />}>Save</ToolbarButton>
</Toolbar>Examples
Rich Text Editor
Undo/redo actions, a multiple-select format toggle group (bold, italic, underline, strikethrough), a single-select alignment group, and list type buttons — the classic inline document editor toolbar.
"use client";
import {
AlignCenterIcon,
AlignJustifyIcon,
AlignLeftIcon,
AlignRightIcon,
BoldIcon,
ItalicIcon,
ListIcon,
ListOrderedIcon,
RedoIcon,
StrikethroughIcon,
UnderlineIcon,
UndoIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
ToggleGroup,
ToggleGroupItem,
} from "@/components/ui/toggle-group";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const formatTools = [
{ icon: BoldIcon, label: "Bold", shortcut: "⌘B" },
{ icon: ItalicIcon, label: "Italic", shortcut: "⌘I" },
{ icon: UnderlineIcon, label: "Underline", shortcut: "⌘U" },
{ icon: StrikethroughIcon, label: "Strikethrough", shortcut: "⌘⇧X" },
] as const;
const alignTools = [
{ icon: AlignLeftIcon, label: "Align left", value: "left" },
{ icon: AlignCenterIcon, label: "Align center", value: "center" },
{ icon: AlignRightIcon, label: "Align right", value: "right" },
{ icon: AlignJustifyIcon, label: "Justify", value: "justify" },
] as const;
const listTools = [
{ icon: ListIcon, label: "Bullet list" },
{ icon: ListOrderedIcon, label: "Numbered list" },
] as const;
export function Pattern() {
return (
<TooltipProvider>
<Toolbar>
<ToolbarGroup>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton render={<Button size="icon" variant="ghost" />}>
<UndoIcon />
</ToolbarButton>
}
/>
<TooltipContent>Undo (⌘Z)</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton render={<Button size="icon" variant="ghost" />}>
<RedoIcon />
</ToolbarButton>
}
/>
<TooltipContent>Redo (⌘⇧Z)</TooltipContent>
</Tooltip>
</ToolbarGroup>
<ToolbarSeparator />
<ToggleGroup className="border-none p-0">
{formatTools.map(({ icon: Icon, label, shortcut }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<ToggleGroupItem value={label.toLowerCase()} />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>
{label}
<span className="ml-1.5 text-muted-foreground">{shortcut}</span>
</TooltipContent>
</Tooltip>
))}
</ToggleGroup>
<ToolbarSeparator />
<ToggleGroup className="border-none p-0" defaultValue={["left"]}>
{alignTools.map(({ icon: Icon, label, value }) => (
<Tooltip key={value}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<ToggleGroupItem value={value} />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</ToggleGroup>
<ToolbarSeparator />
<ToolbarGroup>
{listTools.map(({ icon: Icon, label }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<Button size="icon" variant="ghost" />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}
Image Editor
Transform tools (crop, rotate left, rotate right), zoom controls (zoom out, zoom in, fit to screen), an adjustments button, and an Export action — all icon ghost buttons with individual tooltips.
"use client";
import {
CropIcon,
DownloadIcon,
Maximize2Icon,
RotateCcwIcon,
RotateCwIcon,
SlidersHorizontalIcon,
ZoomInIcon,
ZoomOutIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const transformTools = [
{ icon: CropIcon, label: "Crop" },
{ icon: RotateCcwIcon, label: "Rotate left" },
{ icon: RotateCwIcon, label: "Rotate right" },
] as const;
const zoomTools = [
{ icon: ZoomOutIcon, label: "Zoom out" },
{ icon: ZoomInIcon, label: "Zoom in" },
{ icon: Maximize2Icon, label: "Fit to screen" },
] as const;
export function Pattern() {
return (
<TooltipProvider>
<Toolbar>
<ToolbarGroup>
{transformTools.map(({ icon: Icon, label }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<Button size="icon" variant="ghost" />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
{zoomTools.map(({ icon: Icon, label }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<Button size="icon" variant="ghost" />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Adjustments"
render={<Button size="icon" variant="ghost" />}
>
<SlidersHorizontalIcon />
</ToolbarButton>
}
/>
<TooltipContent>Adjustments</TooltipContent>
</Tooltip>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<ToolbarButton render={<Button size="sm" />}>
<DownloadIcon />
Export
</ToolbarButton>
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}
Data Table
A full-width search input with a leading search icon via ToolbarInput, icon ghost buttons for filter, columns, export, and delete, and a primary "Add row" button on the right end.
"use client";
import {
Columns3Icon,
DownloadIcon,
FilterIcon,
PlusIcon,
SearchIcon,
TrashIcon,
} from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarInput,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const iconActions = [
{ icon: FilterIcon, label: "Filter rows" },
{ icon: Columns3Icon, label: "Manage columns" },
{ icon: DownloadIcon, label: "Export CSV" },
{ icon: TrashIcon, label: "Delete selected" },
] as const;
export function Pattern() {
const [query, setQuery] = useState("");
return (
<TooltipProvider>
<Toolbar className="w-full max-w-lg">
<ToolbarGroup className="flex-1">
<div className="relative flex items-center">
<SearchIcon className="pointer-events-none absolute left-2 size-3.5 text-muted-foreground" />
<ToolbarInput
className="h-7 w-full rounded-md border bg-transparent py-1 pr-2 pl-7 text-xs outline-none placeholder:text-muted-foreground focus:ring-1 focus:ring-ring"
onChange={(e) => setQuery(e.target.value)}
placeholder="Search rows…"
value={query}
/>
</div>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
{iconActions.map(({ icon: Icon, label }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<Button size="icon" variant="ghost" />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<ToolbarButton render={<Button size="sm" />}>
<PlusIcon className="size-3.5" />
Add row
</ToolbarButton>
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}
Code Editor
A language Select dropdown, a word-wrap ToggleGroupItem, a copy icon button that switches to a check on success, and a loading-aware "Run" button — the essential code snippet toolbar.
"use client";
import { CheckIcon, CopyIcon, PlayIcon, WrapTextIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
ToggleGroup,
ToggleGroupItem,
} from "@/components/ui/toggle-group";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const languages = [
{ label: "TypeScript", value: "typescript" },
{ label: "JavaScript", value: "javascript" },
{ label: "Python", value: "python" },
{ label: "Rust", value: "rust" },
{ label: "Go", value: "go" },
];
const SNIPPET = `function greet(name: string): string {
return \`Hello, \${name}!\`;
}`;
export function Pattern() {
const [copied, setCopied] = useState(false);
const [running, setRunning] = useState(false);
const copy = () => {
void navigator.clipboard.writeText(SNIPPET);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
};
const run = () => {
setRunning(true);
setTimeout(() => setRunning(false), 1000);
};
return (
<TooltipProvider>
<Toolbar className="w-full max-w-md">
<ToolbarGroup>
<Select defaultValue="typescript" items={languages}>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
render={
<SelectTrigger className="h-7 w-32 text-xs">
<SelectValue />
</SelectTrigger>
}
/>
}
/>
<TooltipContent>Select language</TooltipContent>
</Tooltip>
<SelectPopup>
{languages.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectPopup>
</Select>
</ToolbarGroup>
<ToolbarSeparator />
<ToggleGroup className="border-none p-0">
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Toggle word wrap"
render={<ToggleGroupItem value="wrap" />}
>
<WrapTextIcon />
</ToolbarButton>
}
/>
<TooltipContent>Word wrap</TooltipContent>
</Tooltip>
</ToggleGroup>
<ToolbarSeparator />
<ToolbarGroup>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Copy code"
onClick={copy}
render={<Button size="icon" variant="ghost" />}
>
{copied ? (
<CheckIcon className="text-emerald-500" />
) : (
<CopyIcon />
)}
</ToolbarButton>
}
/>
<TooltipContent>{copied ? "Copied!" : "Copy code"}</TooltipContent>
</Tooltip>
<ToolbarButton
onClick={run}
render={
<Button
className="gap-1.5"
disabled={running}
loading={running}
size="sm"
/>
}
>
{!running && <PlayIcon className="size-3.5" />}
{running ? "Running…" : "Run"}
</ToolbarButton>
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}
Document Collaboration
Document title on the left, an edit/preview ToggleGroup in the center, comment/history/more icon buttons, and a "Share" primary button — a justify-between layout for real-time doc editing UIs.
"use client";
import {
ClockIcon,
EyeIcon,
MessageSquareIcon,
MoreHorizontalIcon,
PenLineIcon,
Share2Icon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
ToggleGroup,
ToggleGroupItem,
} from "@/components/ui/toggle-group";
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const docActions = [
{ icon: MessageSquareIcon, label: "Comments" },
{ icon: ClockIcon, label: "Version history" },
{ icon: MoreHorizontalIcon, label: "More options" },
] as const;
export function Pattern() {
return (
<TooltipProvider>
<Toolbar className="w-full max-w-lg justify-between">
<ToolbarGroup>
<span className="truncate px-1 font-medium text-sm">
Product Roadmap Q3
</span>
</ToolbarGroup>
<ToolbarGroup className="gap-2">
<ToggleGroup className="border-none p-0" defaultValue={["edit"]}>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Edit mode"
render={<ToggleGroupItem value="edit" />}
>
<PenLineIcon className="size-3.5" />
<span className="text-xs">Edit</span>
</ToolbarButton>
}
/>
<TooltipContent>Switch to edit mode</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<ToolbarButton
aria-label="Preview mode"
render={<ToggleGroupItem value="preview" />}
>
<EyeIcon className="size-3.5" />
<span className="text-xs">Preview</span>
</ToolbarButton>
}
/>
<TooltipContent>Switch to preview mode</TooltipContent>
</Tooltip>
</ToggleGroup>
<ToolbarSeparator />
{docActions.map(({ icon: Icon, label }) => (
<Tooltip key={label}>
<TooltipTrigger
render={
<ToolbarButton
aria-label={label}
render={<Button size="icon" variant="ghost" />}
>
<Icon />
</ToolbarButton>
}
/>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
<ToolbarSeparator />
<ToolbarButton render={<Button size="sm" />}>
<Share2Icon className="size-3.5" />
Share
</ToolbarButton>
</ToolbarGroup>
</Toolbar>
</TooltipProvider>
);
}

