Skip to content
goodtabs UI
BrowseBrowse components

Icon button

An icon only button with a required accessible label and a tooltip that shows it.

npx shadcn@latest add https://ui.goodtabs.co/r/icon-button.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/icon-button

Ghost, outline and default variants. Hover or focus to see the label.

Sizes

The four icon sizes from shadcn's Button: 24, 28, 32 and 36 pixels.

Without a tooltip

12 new resources added this week

A dismiss button in a notice. The label stays as the accessible name.

Usage

import { IconButton } from "@/components/ui/icon-button"
import { PencilIcon } from "lucide-react"

<IconButton label="Edit" variant="outline">
  <PencilIcon />
</IconButton>

Props

PropTypeDefaultDescription
labelstringAccessible name. Becomes the aria-label and the tooltip text.
tooltipbooleantrueShow the label in a tooltip on hover and focus.
sizeButton size"icon-sm"Any Button size. The icon sizes are icon-xs, icon-sm, icon and icon-lg.
variantButton variant"default"Any shadcn Button variant. Ghost suits toolbars, outline suits rows.
childrenReactNodeThe icon, usually one lucide icon. It is sized by the button.

Source

icon-button/icon-button.tsx
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";

type IconButtonProps = Omit<React.ComponentProps<typeof Button>, "aria-label"> & {
  /** Accessible name. Becomes the aria-label and the tooltip text. */
  label: string;
  /** Show the label in a tooltip on hover and focus. */
  tooltip?: boolean;
};

/**
 * A button that shows one icon and always says what it does.
 * The label is the accessible name; the tooltip shows it to everyone else.
 */
export function IconButton({
  label,
  tooltip = true,
  size = "icon-sm",
  type = "button",
  children,
  ...props
}: IconButtonProps) {
  const button = (
    <Button type={type} size={size} aria-label={label} data-slot="icon-button" {...props}>
      {children}
    </Button>
  );

  if (!tooltip) return button;

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>{button}</TooltipTrigger>
        <TooltipContent>{label}</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

For AI agents

Hand your agent this page, the markdown at /components/icon-button/prompt.md, or the Copy AI prompt button above.

When to use

  • Row actions, card corners and toolbars where a text label would crowd the layout
  • Any button whose icon is common enough to read on its own: close, edit, more, add
  • When you want the tooltip and the accessible name to always agree

When not to use

  • For primary actions on a page: those get a text label
  • For an icon whose meaning is not obvious; use a Button with text and an icon instead
  • When a toggle is needed; use Toggle or aria-pressed on a plain Button

Agent notes

  • It extends shadcn's Button, so every Button prop and variant applies; size defaults to icon-sm (28px)
  • label is required and is the accessible name; never pass aria-label separately
  • Each button wraps its own TooltipProvider, so it works without a provider at the root
  • Pass tooltip={false} when the button sits where a tooltip would get in the way, such as in a dense list