Propel

Toolbar

A row of grouped controls with a toolbar role and roving keyboard focus.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import { Menu, MenuContent, MenuItem } from "@makeplane/propel/components/menu";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarMenuTrigger,
  ToolbarSeparator,
  ToolbarToggle,
  ToolbarToggleGroup,
} from "@makeplane/propel/components/toolbar";
import {
  AlignCenterOutline,
  AlignLeftOutline,
  AlignRightOutline,
  BoldOutline,
  CodeOutline,
  ImageOutline,
  ItalicOutline,
  LinkOutline,
  ListOutline,
  ToggleListOutline,
  NumberedListOutline,
  ChatOutline,
  QuoteOutline,
  StrikethroughOutline,
  TableEditorOutline,
  UnderlineOutline,
} from "@makeplane/propel/icons";

const TEXT_STYLES = ["Paragraph", "Heading 1", "Heading 2", "Heading 3"];
const FONTS = ["Sans", "Serif", "Mono"];

export default function BasicDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Formatting">
      <Menu>
        <ToolbarMenuTrigger aria-label="Text style" label="Text" />
        <MenuContent>
          {TEXT_STYLES.map((style) => (
            <MenuItem key={style} label={style} />
          ))}
        </MenuContent>
      </Menu>
      <Menu>
        <ToolbarMenuTrigger aria-label="Font" label="Aa" />
        <MenuContent>
          {FONTS.map((font) => (
            <MenuItem key={font} label={font} />
          ))}
        </MenuContent>
      </Menu>
      <ToolbarButton aria-label="Comment" icon={<Icon icon={ChatOutline} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
        <ToolbarToggle aria-label="Strikethrough" icon={<Icon icon={StrikethroughOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggleGroup defaultValue={["left"]} aria-label="Text alignment">
        <ToolbarToggle
          value="left"
          aria-label="Align left"
          icon={<Icon icon={AlignLeftOutline} />}
        />
        <ToolbarToggle
          value="center"
          aria-label="Align center"
          icon={<Icon icon={AlignCenterOutline} />}
        />
        <ToolbarToggle
          value="right"
          aria-label="Align right"
          icon={<Icon icon={AlignRightOutline} />}
        />
      </ToolbarToggleGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Lists">
        <ToolbarToggle aria-label="Bullet list" icon={<Icon icon={ListOutline} />} />
        <ToolbarToggle aria-label="Numbered list" icon={<Icon icon={NumberedListOutline} />} />
        <ToolbarToggle aria-label="Checklist" icon={<Icon icon={ToggleListOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Blocks">
        <ToolbarToggle aria-label="Quote" icon={<Icon icon={QuoteOutline} />} />
        <ToolbarToggle aria-label="Code block" icon={<Icon icon={CodeOutline} />} />
        <ToolbarButton aria-label="Insert table" icon={<Icon icon={TableEditorOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
      <ToolbarButton aria-label="Insert image" icon={<Icon icon={ImageOutline} />} />
    </Toolbar>
  );
}

Installation

import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarInput,
  ToolbarLink,
  ToolbarMenuTrigger,
  ToolbarSeparator,
  ToolbarToggle,
  ToolbarToggleGroup,
} from "@makeplane/propel/components/toolbar";

Usage

import { Icon } from "@makeplane/propel/components/icon";
import { Menu, MenuContent, MenuItem } from "@makeplane/propel/components/menu";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarMenuTrigger,
  ToolbarSeparator,
  ToolbarToggle,
  ToolbarToggleGroup,
} from "@makeplane/propel/components/toolbar";
import {
  AlignCenterOutline,
  AlignLeftOutline,
  AlignRightOutline,
  BoldOutline,
  CodeOutline,
  ImageOutline,
  ItalicOutline,
  LinkOutline,
  ListOutline,
  ToggleListOutline,
  NumberedListOutline,
  ChatOutline,
  QuoteOutline,
  StrikethroughOutline,
  TableEditorOutline,
  UnderlineOutline,
} from "@makeplane/propel/icons";

const TEXT_STYLES = ["Paragraph", "Heading 1", "Heading 2", "Heading 3"];
const FONTS = ["Sans", "Serif", "Mono"];

export default function BasicDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Formatting">
      <Menu>
        <ToolbarMenuTrigger aria-label="Text style" label="Text" />
        <MenuContent>
          {TEXT_STYLES.map((style) => (
            <MenuItem key={style} label={style} />
          ))}
        </MenuContent>
      </Menu>
      <Menu>
        <ToolbarMenuTrigger aria-label="Font" label="Aa" />
        <MenuContent>
          {FONTS.map((font) => (
            <MenuItem key={font} label={font} />
          ))}
        </MenuContent>
      </Menu>
      <ToolbarButton aria-label="Comment" icon={<Icon icon={ChatOutline} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
        <ToolbarToggle aria-label="Strikethrough" icon={<Icon icon={StrikethroughOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggleGroup defaultValue={["left"]} aria-label="Text alignment">
        <ToolbarToggle
          value="left"
          aria-label="Align left"
          icon={<Icon icon={AlignLeftOutline} />}
        />
        <ToolbarToggle
          value="center"
          aria-label="Align center"
          icon={<Icon icon={AlignCenterOutline} />}
        />
        <ToolbarToggle
          value="right"
          aria-label="Align right"
          icon={<Icon icon={AlignRightOutline} />}
        />
      </ToolbarToggleGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Lists">
        <ToolbarToggle aria-label="Bullet list" icon={<Icon icon={ListOutline} />} />
        <ToolbarToggle aria-label="Numbered list" icon={<Icon icon={NumberedListOutline} />} />
        <ToolbarToggle aria-label="Checklist" icon={<Icon icon={ToggleListOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Blocks">
        <ToolbarToggle aria-label="Quote" icon={<Icon icon={QuoteOutline} />} />
        <ToolbarToggle aria-label="Code block" icon={<Icon icon={CodeOutline} />} />
        <ToolbarButton aria-label="Insert table" icon={<Icon icon={TableEditorOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
      <ToolbarButton aria-label="Insert image" icon={<Icon icon={ImageOutline} />} />
    </Toolbar>
  );
}

Examples

Elevations

raised draws its own card (border and shadow) so the toolbar can float over content; flat draws no surface and sits flush inside an existing bar.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import type { ToolbarElevation } from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline, LinkOutline, UnderlineOutline } from "@makeplane/propel/icons";

function Formatting({ elevation }: { elevation: ToolbarElevation }) {
  return (
    <Toolbar elevation={elevation} size="sm" aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
    </Toolbar>
  );
}

export default function ElevationsDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Formatting elevation="raised" />
      <Formatting elevation="flat" />
    </div>
  );
}

Sizes

size sets the control hit targets: sm packs them to 24px, md gives them 28px. Orthogonal to elevation. Figma’s density maps here — compact → sm, comfortable → md (raised floaters use sm).

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import type { ToolbarSize } from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline, LinkOutline, UnderlineOutline } from "@makeplane/propel/icons";

function Formatting({ size }: { size: ToolbarSize }) {
  return (
    <Toolbar elevation="raised" size={size} aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
    </Toolbar>
  );
}

export default function SizesDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Formatting size="sm" />
      <Formatting size="md" />
    </div>
  );
}

With filter

ToolbarInput is an inline filter box that joins the roving tab order, its height tracking the toolbar’s size like every other item.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarGroup,
  ToolbarInput,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline } from "@makeplane/propel/icons";

export default function WithFilterDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Issue actions">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarInput aria-label="Filter issues" placeholder="Filter…" />
    </Toolbar>
  );
}

ToolbarLink is a toolbar item that navigates — an <a> sharing the same roving tab order and icon chrome as the buttons around it. Its required icon takes the bare glyph, so name it with aria-label.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarGroup,
  ToolbarLink,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import { BoldOutline, HistoryOutline, ItalicOutline } from "@makeplane/propel/icons";

export default function WithLinkDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Document actions">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarLink
        href="#"
        onClick={(event) => event.preventDefault()}
        aria-label="View edit history"
        icon={<Icon icon={HistoryOutline} />}
      />
    </Toolbar>
  );
}

With labels

ToolbarButton, ToolbarToggle, and ToolbarLink take an optional label after the icon (e.g. “Comment”, “Link”): the square icon box widens to hug icon + text, with the size’s gap and side padding. The visible label names the control; icon-only items still take aria-label.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarLink,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import {
  BoldOutline,
  HistoryOutline,
  ItalicOutline,
  LinkOutline,
  ChatOutline,
} from "@makeplane/propel/icons";

export default function WithLabelsDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Selection actions">
      <ToolbarButton label="Comment" icon={<Icon icon={ChatOutline} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggle label="Link" icon={<Icon icon={LinkOutline} />} />
      <ToolbarSeparator />
      <ToolbarLink
        href="#"
        onClick={(event) => event.preventDefault()}
        label="History"
        icon={<Icon icon={HistoryOutline} />}
      />
    </Toolbar>
  );
}

With tooltips

Toolbar controls are usually icon-only, so pair each with a Tooltip naming it (plus a dimmed shortcut hint). A shared TooltipProvider keeps open and close timing consistent across the bar.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import { Toolbar, ToolbarToggle } from "@makeplane/propel/components/toolbar";
import { Tooltip, TooltipProvider } from "@makeplane/propel/components/tooltip";
import { BoldOutline, ItalicOutline, UnderlineOutline } from "@makeplane/propel/icons";

export default function WithTooltipsDemo() {
  return (
    <TooltipProvider>
      <Toolbar elevation="raised" size="sm" aria-label="Text formatting">
        <Tooltip label="Bold" shortcut="⌘ B">
          <ToolbarToggle
            aria-label="Bold"
            aria-keyshortcuts="Meta+B"
            icon={<Icon icon={BoldOutline} />}
          />
        </Tooltip>
        <Tooltip label="Italic" shortcut="⌘ I">
          <ToolbarToggle
            aria-label="Italic"
            aria-keyshortcuts="Meta+I"
            icon={<Icon icon={ItalicOutline} />}
          />
        </Tooltip>
        <Tooltip label="Underline" shortcut="⌘ U">
          <ToolbarToggle
            aria-label="Underline"
            aria-keyshortcuts="Meta+U"
            icon={<Icon icon={UnderlineOutline} />}
          />
        </Tooltip>
      </Toolbar>
    </TooltipProvider>
  );
}

API Reference

Toolbar

The ready-made toolbar: grafts Base UI's Toolbar.Root behavior (the toolbar role + roving focus, plus disabled / loopFocus) onto the styled horizontal elements/toolbar row and shares its size with the controls inside via context (so each control packs to match).

PropTypeDefaultDescription
size(required)"md" | "sm"—Control hit-target scale: sm (24px) or md (28px). Orthogonal to elevation. Shared with the controls inside via context. Figma's density maps here — compact → sm, comfortable → md (raised floaters use sm).
elevation(required)"raised" | "flat"—Whether the toolbar draws its own surface (raised) or sits flush (flat).
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar.

ToolbarGroup

Groups related controls: grafts Base UI's Toolbar.Group behavior (the group role + group disabled) onto the styled ToolbarGroup cluster. The group shares the toolbar row's gap-2 per Figma, so it is a semantic grouping — pair it with ToolbarSeparators to make it visible.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar group.

ToolbarSeparator

A thin vertical rule that visually divides one cluster of controls from the next: grafts Base UI's Toolbar.Separator behavior (the separator role) onto the styled rule.

PropTypeDefaultDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar separator.

ToolbarButton

A toolbar action button: grafts Base UI's Toolbar.Button (roving-focus item) behavior onto the styled ToolbarButton, taking its size from the surrounding Toolbar via context.

PropTypeDefaultDescription
size"md" | "sm"—Size override; defaults to the surrounding Toolbar's size.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar button.
labelstring—Visible text after the icon (e.g. "Comment"); widens the square icon box to fit.
aria-labelstring—Accessible name; omit to use the visible label. Accessible name for the icon button.
iconReactNode—Icon element rendered inside the toolbar button.

ToolbarToggle

A two-state toolbar button for formatting toggles like bold or italic. Grafts Base UI's Toolbar.Button (roving-focus item) and Toggle (pressed state) behaviors onto the styled ToolbarButton, and takes its size from the surrounding Toolbar via context.

PropTypeDefaultDescription
size"md" | "sm"—Size override; defaults to the surrounding Toolbar's size.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar button.
labelstring—Visible text after the icon (e.g. "Comment"); widens the square icon box to fit.
aria-labelstring—Accessible name; omit to use the visible label. Accessible name for the icon toggle.
iconReactNode—Icon element rendered inside the toolbar toggle.

ToolbarToggleGroup

A cluster of ToolbarToggles sharing one selection: grafts Base UI Toolbar.Group (toolbar group semantics + group disabled) and ToggleGroup (value state) onto the styled toggle-group container. Drive it with value/defaultValue + onValueChange. An aria-label is required.

PropTypeDefaultDescription
aria-label(required)string—Accessible name for the toggle cluster.

ToolbarLink

A toolbar item that navigates — Base UI's Toolbar.Link (<a> in the roving tab order) grafted onto the toolbar item chrome. Pass an icon element, e.g. <Icon icon={ExternalLink} />. An aria-label is required when the item is icon-only.

PropTypeDefaultDescription
size"md" | "sm"—Size override; defaults to the surrounding Toolbar's size.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar link (e.g. a router Link).
labelstring—Visible text after the icon (e.g. "History"); widens the square icon box to fit.
aria-labelstring—Accessible name; omit to use the visible label. Accessible name for the icon link.
iconReactNode—Icon element rendered inside the toolbar link.

ToolbarInput

An inline text input in the toolbar's roving tab order (e.g. a filter box) — Base UI's Toolbar.Input grafted onto the styled miniature field, its height tracking the toolbar's size. An aria-label is required.

PropTypeDefaultDescription
aria-label(required)string—Accessible name for the field (placeholder is not a name).
size"md" | "sm"—Size override; defaults to the surrounding Toolbar's size.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>—Custom render for the underlying styled toolbar input.

ToolbarMenuTrigger

The trigger that opens a ToolbarMenu: a label plus a disclosure chevron.

PropTypeDefaultDescription
label(required)string—Visible menu-trigger label.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuTriggerState>—Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.