Icon button
Icon-only ghost button. The label is both the tooltip and the accessible name.
Icon button runs an action from an icon alone, for toolbars, headers, and rows where a word
would not fit. It is a ghost Button in a square box,
wrapped in a Tooltip, and its required label becomes
both the tooltip and the name a screen reader announces. It has no fill until the pointer is
over it, and its corners follow the look's rounded-control, so it is a rounded square in
Washi and a circle in ICS.
Usage
import { X } from '@phosphor-icons/react';
import { IconButton } from '@/components/ui/icon-button';
<IconButton icon={X} label="Close" onClick={handleClose} />Each icon button brings its own tooltip provider, so it needs no wrapper. icon takes a
component rather than an element, so set it from a client component, because a Server
Component cannot pass a component to a client one.
The label is the name
label is a required string, and Icon button uses it twice. It is set as the button's
aria-label, so a screen reader announces it, and it is the text of the tooltip that opens on
hover and on keyboard focus. Write it as the action the button takes, like "Close" or "Add to
favorites", rather than the name of the icon. The icon is hidden from assistive tech, and the
button takes no children or aria-label of its own, so the label is the only name it has.
Three sizes
size sets a square box on Button's heights: sm is 36px and the default, md is 40px, and
xl is 56px. Button's default is md, so match the buttons beside it, and a back arrow next
to default-size actions takes md. The glyph is 20px, or 24px at xl, and iconSize sets
your own. On a touch screen the box grows to at least 44px tall and keeps its width, so sm
is 36px wide by 44px tall there.
Where the tooltip opens
tooltipSide opens the tooltip on the top by default, or on the right, bottom, or
left, and the tooltip flips to the opposite side when there is no room. It opens as soon as
the pointer arrives, and tooltipDelay sets a wait in milliseconds. Inside a
TooltipProvider it takes the
provider's delay unless you set one. It also opens when the
button takes keyboard focus, and it closes when the button is pressed, when focus leaves, when
the pointer leaves both the button and the tooltip, or on Escape. On a touch screen a tap runs
the action and the tooltip does not open. Hover each button to see its side.
Props
Icon button takes every <button> attribute except children and aria-label, plus these.
| Prop | Type | Default | What it does |
|---|---|---|---|
icon | ElementType | Required. The icon component to draw, sized to the box. | |
label | string | Required. The accessible name and the tooltip text. | |
size | "sm" | "md" | "xl" | "sm" | A square box 36, 40, or 56px wide, and at least 44px tall on a touch screen. |
iconSize | number | string | Follows size | Sets the icon's width and height, in place of 20px, or 24px at xl. |
tooltipSide | "top" | "right" | "bottom" | "left" | "top" | The side the tooltip opens on, before it flips for room. |
tooltipDelay | number | The provider's, or 0 | Milliseconds the pointer rests on the button before the tooltip opens. |
tone | "neutral" | "primary" | "support" | "danger" | "inherit" | "neutral" | The hue, as on Button. A tone other than neutral draws the icon in the tone's own color, text-tone, and washes the hover, so danger gives a delete the destructive red. |
type | "button" | "submit" | "reset" | "button" | As on <button>, except it never submits a form unless you set "submit". |
It has no variant, loading, or asChild, so it is always a ghost <button>, and its ref
reaches that element.
Accessibility
- It renders a native
<button>, so Enter and Space press it, andtype="button"keeps it from submitting a form by accident. labelis the accessible name, set asaria-label, and the icon isaria-hidden, so the icon adds nothing to the name.- The tooltip opens on keyboard focus as well as on hover, and Escape closes it without moving
focus. While it is open the button points at it with
aria-describedby, and the tooltip hasrole="tooltip". - The pointer can move from the button onto the tooltip without closing it.
- On a touch screen the tooltip does not open, so there the icon carries the meaning alone. Use an icon people already know, or a Button with a word.
disabledsets the native attribute, so the button leaves the tab order and assistive tech reports it as unavailable. It also ignores the pointer, so its tooltip cannot open and a sighted user has only the icon to go on.- Focus shows a 2px ring on
:focus-visibleonly, offset from the edge, in the contrast ink rather than the brand color, so it reads on every look. - In forced colors the button keeps a transparent outline that the system paints, so focus stays visible in Windows high contrast.
- On a touch screen it is at least 44px tall, and it keeps its width, so
smandmdare narrower than 44px there. Usexlwhere a full 44px square matters. - Under reduced motion the hover color changes without a transition, and the tooltip appears without its fade.
When to use something else
- Button when there is room for a word, or for the main
action in a view. Its
iconBeforekeeps the icon and puts the label on screen. - Toggle for an icon that stays on or off, like bold or a pinned filter, because it reports whether it is pressed.
- A link styled with
buttonVariants({ variant: "ghost", size: "icon" })inside a Tooltip, for an icon that navigates. Icon button always renders a<button>, so give the link its ownaria-label.
Install
Installing it also adds Button and Tooltip, which it is built from.
shadcn add @gesso/icon-button