Toolbar
General toolbar layout component for headings, counts, grouped controls, actions, and separators.
Overview
Toolbar provides consistent shell spacing for dense UI rows without owning the
surfaces of the controls inside it. Compose it with Button, Input,
SearchExpand, ToggleGroup, Menu, Breadcrumb, and other components.
size scales toolbar-owned gaps, separators, title text, count text, and the
title trigger. Pass the same size to composed controls when the whole row should
follow one density.
Toolbar.TitleTrigger is an unstyled Button for title-adjacent menu labels.
It stays surface-free and only handles text hover/open color.
Toolbar.Separator renders the Cubitt Separator component with toolbar-sized
vertical heights, so separator color remains context-aware.
Toolbar.Breadcrumb adapts breadcrumb slots for toolbar heading scale while
leaving links, menus, route state, and path data with the consumer.
Usage
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";<Toolbar size="md">
<Toolbar.Heading>
<Toolbar.Title>Files</Toolbar.Title>
<Toolbar.Count>18</Toolbar.Count>
</Toolbar.Heading>
<Toolbar.Actions>{actions}</Toolbar.Actions>
</Toolbar>Examples
Default
import { Button } from "@tilt-legal/cubitt-components/button";
import {
ToggleGroup,
ToggleGroupItem,
} from "@tilt-legal/cubitt-components/toggle-group";
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";
import {
Grid,
Menu,
Plus,
SortArrows,
} from "@tilt-legal/cubitt-icons/ui/outline";
export default function Example() {
return (
<Toolbar size="md">
<Toolbar.Heading>
<Toolbar.Title>Files</Toolbar.Title>
<Toolbar.Count>18</Toolbar.Count>
</Toolbar.Heading>
<Toolbar.Actions>
<Button
aria-label="Sort files"
mode="icon"
size="md"
variant="secondary"
>
<SortArrows />
</Button>
<ToggleGroup
aria-label="View options"
defaultValue="list"
groupVariant="segmented"
size="md"
>
<ToggleGroupItem aria-label="Grid view" mode="icon" value="grid">
<Grid />
</ToggleGroupItem>
<ToggleGroupItem aria-label="List view" mode="icon" value="list">
<Menu />
</ToggleGroupItem>
</ToggleGroup>
<Toolbar.Separator />
<Button size="md">
<Plus />
Upload
</Button>
</Toolbar.Actions>
</Toolbar>
);
}Sizes
import { Button } from "@tilt-legal/cubitt-components/button";
import {
ToggleGroup,
ToggleGroupItem,
} from "@tilt-legal/cubitt-components/toggle-group";
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";
import { Grid, Menu, SortArrows } from "@tilt-legal/cubitt-icons/ui/outline";
export default function Example() {
return (
<div className="grid w-full gap-4">
{(["sm", "md", "lg"] as const).map((size) => (
<Toolbar key={size} size={size}>
<Toolbar.Heading>
<Toolbar.Title>{size.toUpperCase()} toolbar</Toolbar.Title>
<Toolbar.Count>12</Toolbar.Count>
</Toolbar.Heading>
<Toolbar.Actions>
<Button
aria-label="Sort files"
mode="icon"
size={size}
variant="secondary"
>
<SortArrows />
</Button>
<ToggleGroup
aria-label="View options"
defaultValue="list"
groupVariant="segmented"
size={size}
>
<ToggleGroupItem aria-label="Grid view" mode="icon" value="grid">
<Grid />
</ToggleGroupItem>
<ToggleGroupItem aria-label="List view" mode="icon" value="list">
<Menu />
</ToggleGroupItem>
</ToggleGroup>
<Toolbar.Separator />
<Button size={size}>Action</Button>
</Toolbar.Actions>
</Toolbar>
))}
</div>
);
}With Search
import { useState } from "react";
import { Button } from "@tilt-legal/cubitt-components/button";
import { Input } from "@tilt-legal/cubitt-components/input";
import { SearchExpand } from "@tilt-legal/cubitt-components/search-expand";
import {
ToggleGroup,
ToggleGroupItem,
} from "@tilt-legal/cubitt-components/toggle-group";
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";
import { Grid, Menu, Plus, Xmark } from "@tilt-legal/cubitt-icons/ui/outline";
export default function Example() {
const [query, setQuery] = useState("");
return (
<Toolbar size="md">
<Toolbar.Heading>
<Toolbar.Title>Documents</Toolbar.Title>
<Toolbar.Count>42</Toolbar.Count>
</Toolbar.Heading>
<Toolbar.Actions>
<SearchExpand collapseOnBlurWhenEmpty>
<Input
aria-label="Search files"
onChange={(event) => setQuery(event.currentTarget.value)}
placeholder="Search files"
size="md"
value={query}
/>
<Button
className="-me-2"
disabled={!query}
mode="icon"
onClick={() => setQuery("")}
size="md"
type="button"
variant="link"
>
{query ? <Xmark /> : null}
</Button>
</SearchExpand>
<ToggleGroup
aria-label="View options"
defaultValue="list"
groupVariant="segmented"
size="md"
>
<ToggleGroupItem aria-label="Grid view" mode="icon" value="grid">
<Grid />
</ToggleGroupItem>
<ToggleGroupItem aria-label="List view" mode="icon" value="list">
<Menu />
</ToggleGroupItem>
</ToggleGroup>
<Toolbar.Separator />
<Button size="md" variant="secondary">
<Plus />
Add
</Button>
</Toolbar.Actions>
</Toolbar>
);
}Breadcrumb
import {
BreadcrumbIcon,
BreadcrumbItem,
BreadcrumbLabel,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbOverflowItem,
BreadcrumbOverflowMenu,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@tilt-legal/cubitt-components/breadcrumb";
import { Button } from "@tilt-legal/cubitt-components/button";
import { FilesFolderIcon } from "@tilt-legal/cubitt-components/files";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@tilt-legal/cubitt-components/tooltip";
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";
import { Plus, SortArrows } from "@tilt-legal/cubitt-icons/ui/outline";
export default function Example() {
const hiddenFolders = [
{ id: "matter", label: "Matter" },
{ id: "disclosure", label: "Disclosure" },
];
return (
<Toolbar className="max-w-4xl" size="md">
<Toolbar.Heading className="w-[28rem]">
<Toolbar.Breadcrumb className="@container w-full">
<BreadcrumbList>
<BreadcrumbItem className="shrink-0">
<BreadcrumbLink href="/">
<BreadcrumbLabel className="shrink-0">Files</BreadcrumbLabel>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>/</BreadcrumbSeparator>
<BreadcrumbItem className="hidden @max-[24rem]:inline-flex">
<BreadcrumbOverflowMenu>
{hiddenFolders.map((folder) => (
<BreadcrumbOverflowItem key={folder.id}>
<FilesFolderIcon />
{folder.label}
</BreadcrumbOverflowItem>
))}
</BreadcrumbOverflowMenu>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden @max-[24rem]:inline-flex">
/
</BreadcrumbSeparator>
<BreadcrumbItem className="shrink-0 @max-[24rem]:hidden">
<Tooltip delay={400}>
<TooltipTrigger
render={
<BreadcrumbLink href="/">
<BreadcrumbIcon>
<FilesFolderIcon />
</BreadcrumbIcon>
<BreadcrumbLabel className="shrink-0 @max-[34rem]:sr-only">
Matter
</BreadcrumbLabel>
</BreadcrumbLink>
}
/>
<TooltipContent side="bottom">Matter</TooltipContent>
</Tooltip>
</BreadcrumbItem>
<BreadcrumbSeparator className="@max-[24rem]:hidden">
/
</BreadcrumbSeparator>
<BreadcrumbItem className="shrink-0 @max-[24rem]:hidden">
<BreadcrumbLink href="/">
<BreadcrumbIcon>
<FilesFolderIcon />
</BreadcrumbIcon>
<BreadcrumbLabel className="shrink-0">
Disclosure
</BreadcrumbLabel>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="@max-[24rem]:hidden">
/
</BreadcrumbSeparator>
<BreadcrumbItem className="shrink-0">
<BreadcrumbPage>
<BreadcrumbIcon>
<FilesFolderIcon />
</BreadcrumbIcon>
<BreadcrumbLabel className="shrink-0">
Closing Set
</BreadcrumbLabel>
</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Toolbar.Breadcrumb>
</Toolbar.Heading>
<Toolbar.Actions>
<Button
aria-label="Sort files"
mode="icon"
size="md"
variant="secondary"
>
<SortArrows />
</Button>
<Button size="md">
<Plus />
Upload
</Button>
</Toolbar.Actions>
</Toolbar>
);
}Title Trigger
import { useState } from "react";
import { Button } from "@tilt-legal/cubitt-components/button";
import {
Menu,
MenuContent,
MenuRadioGroup,
MenuRadioItem,
MenuTrigger,
} from "@tilt-legal/cubitt-components/menu";
import { Toolbar } from "@tilt-legal/cubitt-components/toolbar";
import { Dots } from "@tilt-legal/cubitt-icons/ui/outline";
export default function Example() {
const [activePanel, setActivePanel] = useState("files");
const panels = [
{ id: "files", label: "Files" },
{ id: "instructions", label: "Instructions" },
{ id: "links", label: "Links" },
];
const activeLabel =
panels.find((panel) => panel.id === activePanel)?.label ?? "";
return (
<Toolbar>
<Toolbar.Heading>
<Toolbar.Title>Attach</Toolbar.Title>
<Menu>
<MenuTrigger
render={<Toolbar.TitleTrigger>{activeLabel}</Toolbar.TitleTrigger>}
/>
<MenuContent align="start">
<MenuRadioGroup onValueChange={setActivePanel} value={activePanel}>
{panels.map((panel) => (
<MenuRadioItem closeOnClick key={panel.id} value={panel.id}>
{panel.label}
</MenuRadioItem>
))}
</MenuRadioGroup>
</MenuContent>
</Menu>
</Toolbar.Heading>
<Toolbar.Group>
<Button aria-label="More actions" mode="icon" variant="secondary">
<Dots />
</Button>
<Button>Attach</Button>
</Toolbar.Group>
</Toolbar>
);
}API Reference
Toolbar
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "md" | Scales toolbar-owned spacing, separator height, title text, count text, and title trigger text. Pass matching sizes to composed controls. |
wrap | boolean | false | Allows toolbar contents to wrap when horizontal space is constrained. |
className | string | - | Overrides or extends root toolbar styles. |
children | ReactNode | - | Toolbar content. |
Also accepts standard div props.
Toolbar.Heading
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends the heading region. |
children | ReactNode | - | Usually Toolbar.Title, Toolbar.TitleTrigger, and Toolbar.Count. |
Also accepts standard div props.
Toolbar.Title
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends title text styles, including size when needed. |
children | ReactNode | - | Title content. |
Also accepts standard div props.
Toolbar.Breadcrumb
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends toolbar breadcrumb layout and scale styles. |
children | ReactNode | - | Primitive breadcrumb composition, usually BreadcrumbList and its child slots. |
Also accepts primitive Breadcrumb props.
Toolbar.TitleTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
showChevron | boolean | true | Shows the trailing chevron. |
chevron | ReactNode | <ChevronDown /> | Custom trailing indicator. |
className | string | - | Overrides or extends the title trigger text styles. |
children | ReactNode | - | Active adjacent label. |
Also accepts Button props. The trigger uses Button's unstyled mode internally.
Toolbar.Count
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends count text styles. |
children | ReactNode | - | Count content. |
Also accepts standard span props.
Toolbar.Group And Toolbar.Actions
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends the control group. |
children | ReactNode | - | Grouped controls. |
Both accept standard div props.
Toolbar.Spacer
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Overrides or extends the flexible spacer. |
Also accepts standard div props.
Toolbar.Separator
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "vertical" | Separator direction. Vertical separators get toolbar-sized heights. |
className | string | - | Overrides or extends separator styles. |
Also accepts Cubitt Separator props.