Interactive Toolbar

Published Nov 3, 2023

Building this toolbar started with a simple frustration โ€” I was tired of seeing toolbars that felt disconnected from their actions. While working on a Knowledge Base project, I noticed users struggling to find basic formatting controls. They were there, but not where users expected them to be, making the experience less intuitive.

Play around with the toolbar below to see how it appears at different screen positions.

Getting the grouping right was trickier than I anticipated. In the early versions, there were no separators, making it hard for users to mentally chunk related actions. Adding those subtle dividers made a surprising difference. Suddenly, users could navigate the toolbar much more efficiently, and their feedback was overwhelmingly positive.

One detail I'm particularly happy with is how the hover states work. I wanted them to be subtle enough not to be distracting, yet clear enough to indicate interactivity. It took several iterations to find that sweet spot.

This component (with different layouts) has since become a key component in our projects, consistently enhancing the user experience without overwhelming them.

Overview

Modern applications require intuitive command interfaces. This toolbar provides:

Use Cases


Usage

import {Bell, Bold, FolderOpen, Heart, Inbox, Italic, MessageCircle, Strikethrough} from "lucide-react";
 
import {
  Toolbar,
  ToolbarButton,
  ToolbarButtonLink,
  ToolbarLink,
  ToolbarPortal,
  ToolbarSeparator,
  ToolbarToggleGroup,
  ToolbarToggleItem,
} from "@/components/ui/toolbar";
 
const items = [
  {
    id: 0,
    type: "link",
    label: "Notifications",
    icon: Bell,
  },
  {
    id: 1,
    type: "item",
    label: "Inbox",
    icon: Inbox,
  },
  {
    id: 2,
    type: "separator",
  },
  {
    id: 3,
    type: "item",
    label: "Likes",
    icon: Heart,
  },
  {
    id: 4,
    type: "item",
    label: "Files",
    icon: FolderOpen,
  },
];
 
const ToolbarComponent = () => {
  return (
    <ToolbarPortal>
      <Toolbar orientation="horizontal" position={"bottom"}>
        {items.map((item, index) =>
          item.type === "item" ? (
            <ToolbarButton key={item.id}>
              {item?.icon ? <item.icon size={16} /> : null}
            </ToolbarButton>
          ) : item.type === "link" ? (
            <ToolbarButtonLink href={""} key={item.id}>
              {item?.icon ? <item.icon size={16} /> : null}
            </ToolbarButtonLink>
          ) : (
            <ToolbarSeparator key={item.id} />
          )
        )}
        <ToolbarSeparator />
        <ToolbarButton size={"text"} label="Comments" variant={"destructive"}>
          <MessageCircle size={16} />
        </ToolbarButton>
        <ToolbarSeparator />
        <ToolbarToggleGroup type={"single"}>
          <ToolbarToggleItem value="bold">
            <Bold size={16} />
          </ToolbarToggleItem>
          <ToolbarToggleItem value="italic">
            <Italic size={16} />
          </ToolbarToggleItem>
          <ToolbarToggleItem value="strike">
            <Strikethrough size={16} />
          </ToolbarToggleItem>
        </ToolbarToggleGroup>
        <ToolbarSeparator />
        <ToolbarLink>Edited 2 hours ago</ToolbarLink>
      </Toolbar>
    </ToolbarPortal>
  );
};
 
export default ToolbarComponent;

Installation

npm install @radix-ui/react-toolbar @radix-ui/react-portal class-variance-authority framer-motion

API

type ToolbarProps = {
  orientation: "horizontal" | "vertical";
  position: "top" | "bottom" | "left" | "right";
}; 

09:00:04 PM

24th of January, 2025