Skip to main content

Button Components

Excalidraw provides several button components that follow the editor’s design system. These components ensure visual consistency and provide common button patterns used throughout the editor.

Button

The main Button component is a generic button that accepts all standard HTML button props and adds Excalidraw styling.

Basic Usage

import { Button } from "@excalidraw/excalidraw";

function MyComponent() {
  const handleClick = () => {
    console.log("Button clicked");
  };

  return (
    <Button onSelect={handleClick}>
      Click Me
    </Button>
  );
}

Props

onSelect
function
required
Callback function executed when the button is clicked.
onSelect: () => any;
selected
boolean
Whether the button is in an active/selected state. Adds visual styling to indicate selection.
children
React.ReactNode
required
The button content (text, icons, or other elements).
type
'button' | 'submit' | 'reset'
default:"'button'"
The HTML button type attribute.
className
string
Additional CSS class names to apply to the button.
...rest
HTMLButtonElement props
All standard HTML button attributes are supported (disabled, title, aria-label, etc.).

Styling

The Button component uses the .excalidraw-button CSS class:
.excalidraw-button {
  padding: 0.5rem 1rem;
  border: 1px solid #e5e7eb;
  border-radius: 0.375rem;
  background: white;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.875rem;
  transition: all 0.2s;
}

.excalidraw-button:hover {
  background: #f9fafb;
}

.excalidraw-button.selected {
  background: #e0e7ff;
  border-color: #6965db;
}

.excalidraw-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

ButtonIcon

A specialized button component for icon-only buttons, commonly used in toolbars.

Basic Usage

import { ButtonIcon } from "@excalidraw/excalidraw";
import { TrashIcon } from "./icons";

function MyComponent() {
  const handleDelete = (event) => {
    console.log("Delete clicked");
  };

  return (
    <ButtonIcon
      icon={TrashIcon}
      title="Delete"
      onClick={handleDelete}
    />
  );
}

Props

icon
JSX.Element
required
The icon element to display in the button.
title
string
required
Tooltip text shown on hover (also used for accessibility).
onClick
function
required
Callback function when the button is clicked.
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
active
boolean
Whether the button is in an active state (e.g., tool currently selected).
standalone
boolean
When true, applies standalone styling that may be better suited for isolated buttons outside of toolbars.
className
string
Additional CSS class names to apply.
testId
string
Test identifier for automated testing (sets data-testid attribute).
style
React.CSSProperties
Inline styles to apply to the button.

Complete Examples

import { Button } from "@excalidraw/excalidraw";
import { useState } from "react";

function AlignmentButtons() {
  const [alignment, setAlignment] = useState("left");

  return (
    <div style={{ display: "flex", gap: "0.5rem" }}>
      <Button
        onSelect={() => setAlignment("left")}
        selected={alignment === "left"}
      >
        Left
      </Button>
      <Button
        onSelect={() => setAlignment("center")}
        selected={alignment === "center"}
      >
        Center
      </Button>
      <Button
        onSelect={() => setAlignment("right")}
        selected={alignment === "right"}
      >
        Right
      </Button>
    </div>
  );
}

Accessibility

Both button components follow accessibility best practices:

Button

  • Native <button> element for proper keyboard navigation
  • Accepts aria-label for screen readers
  • Responds to Enter and Space keys
  • :focus styles for keyboard users

ButtonIcon

  • Uses title prop for tooltip and aria-label
  • Icon-only buttons always have descriptive titles
  • Active state is communicated visually and semantically
  • Supports keyboard navigation

Best Practices

{/* Good: Has descriptive title */}
<ButtonIcon
  icon={<TrashIcon />}
  title="Delete selected elements"
  onClick={handleDelete}
/>

{/* Good: Disabled state explained */}
<Button 
  onSelect={handleSave}
  disabled={!hasChanges}
  title={!hasChanges ? "No changes to save" : "Save changes"}
>
  Save
</Button>

{/* Good: ARIA label for complex actions */}
<Button
  onSelect={handleExport}
  aria-label="Export drawing as PNG image"
>
  <DownloadIcon /> Export
</Button>

Styling Patterns

Primary Action Button

<Button
  onSelect={handleAction}
  className="primary-button"
  style={{
    backgroundColor: "#6965db",
    color: "white",
    border: "none",
    fontWeight: 500,
  }}
>
  Primary Action
</Button>

Danger/Destructive Button

<Button
  onSelect={handleDelete}
  className="danger-button"
  style={{
    backgroundColor: "#dc2626",
    color: "white",
    border: "none",
  }}
>
  Delete
</Button>

Ghost Button (Minimal)

<Button
  onSelect={handleCancel}
  className="ghost-button"
  style={{
    background: "transparent",
    border: "none",
    color: "#6b7280",
  }}
>
  Cancel
</Button>

Icon Button with Badge

<div style={{ position: "relative" }}>
  <ButtonIcon
    icon={NotificationIcon}
    title="Notifications"
    onClick={openNotifications}
  />
  {notificationCount > 0 && (
    <span style={{
      position: "absolute",
      top: -4,
      right: -4,
      background: "#dc2626",
      color: "white",
      borderRadius: "50%",
      width: 16,
      height: 16,
      fontSize: 10,
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
    }}>
      {notificationCount}
    </span>
  )}
</div>

See Also