Propel

Toolbar

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

Basic

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 {
  AlignCenter,
  AlignLeft,
  AlignRight,
  Bold,
  Code,
  Image,
  Italic,
  Link,
  List,
  ListChecks,
  ListOrdered,
  MessageSquare,
  Quote,
  Strikethrough,
  Table,
  Underline,
} from "lucide-react";

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

export default function BasicDemo() {
  return (
    <Toolbar elevation="raised" density="compact">
      <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={MessageSquare} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={Bold} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={Italic} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={Underline} />} />
        <ToolbarToggle aria-label="Strikethrough" icon={<Icon icon={Strikethrough} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggleGroup defaultValue={["left"]} aria-label="Text alignment">
        <ToolbarToggle value="left" aria-label="Align left" icon={<Icon icon={AlignLeft} />} />
        <ToolbarToggle
          value="center"
          aria-label="Align center"
          icon={<Icon icon={AlignCenter} />}
        />
        <ToolbarToggle value="right" aria-label="Align right" icon={<Icon icon={AlignRight} />} />
      </ToolbarToggleGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Lists">
        <ToolbarToggle aria-label="Bullet list" icon={<Icon icon={List} />} />
        <ToolbarToggle aria-label="Numbered list" icon={<Icon icon={ListOrdered} />} />
        <ToolbarToggle aria-label="Checklist" icon={<Icon icon={ListChecks} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Blocks">
        <ToolbarToggle aria-label="Quote" icon={<Icon icon={Quote} />} />
        <ToolbarToggle aria-label="Code block" icon={<Icon icon={Code} />} />
        <ToolbarButton aria-label="Insert table" icon={<Icon icon={Table} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={Link} />} />
      <ToolbarButton aria-label="Insert image" icon={<Icon icon={Image} />} />
    </Toolbar>
  );
}

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 { Bold, Italic, Link, Underline } from "lucide-react";

function Formatting({ elevation }: { elevation: ToolbarElevation }) {
  return (
    <Toolbar elevation={elevation} density="compact" aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={Bold} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={Italic} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={Underline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={Link} />} />
    </Toolbar>
  );
}

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

Densities

density sets the control hit targets: compact packs them to 24px, comfortable gives them 28px.

Show code
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import type { ToolbarDensity } from "@makeplane/propel/components/toolbar";
import { Bold, Italic, Link, Underline } from "lucide-react";

function Formatting({ density }: { density: ToolbarDensity }) {
  return (
    <Toolbar elevation="raised" density={density} aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={Bold} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={Italic} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={Underline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={Link} />} />
    </Toolbar>
  );
}

export default function DensitiesDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Formatting density="compact" />
      <Formatting density="comfortable" />
    </div>
  );
}

With filter

ToolbarInput is an inline filter box that joins the roving tab order, its height tracking the toolbar’s density 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 { Bold, Italic } from "lucide-react";

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

With tooltips

Toolbar controls are 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 { Bold, Italic, Underline } from "lucide-react";

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

Installation

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

Props

Toolbar

PropTypeRequiredDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>NoAllows 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.
elevation"raised" | "flat"Yes
density"compact" | "comfortable"Yes

ToolbarButton

PropTypeRequiredDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>NoAllows 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.
density"compact" | "comfortable"NoDensity override; defaults to the surrounding `Toolbar`'s density.
iconReactNodeYesIcon element rendered inside the toolbar button.

ToolbarToggle

PropTypeRequiredDescription
density"compact" | "comfortable"NoDensity override; defaults to the surrounding `Toolbar`'s density.
iconReactNodeYesIcon element rendered inside the toolbar toggle.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>NoCustom render for the underlying styled toolbar button.

ToolbarMenuTrigger

PropTypeRequiredDescription
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuTriggerState>NoAllows 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.
labelstringYesVisible menu-trigger label.