Skip to main content

Icon Button

Initiates an action, such as completing a task or submitting information. Renders one icon and nothing else, with a required label prop that carries the accessible name.

Every IconButton states what it means: appearance (visual weight) and intent (tone) are required; there are no implicit defaults. intent takes "neutral" alone: an icon carries no text to name the action a tone colors, so IconButton draws no accent or danger tone.

Appearance

IconButton draws all three of Button's box appearances, and each one matches its Button twin. link stays off the union — an icon-only link has no text to read as a link. An icon button sits beside a button with no color or weight seam.

import { IconButton } from "@ngrok/mantle/button";import { GlobeIcon } from "@phosphor-icons/react/Globe"; <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" icon={<GlobeIcon />} />

Size

Use the size prop to control the box size of the icon button, default md. The scale is shared with Button and SplitButton: the same size name always renders the same box height, so mixed compositions stay aligned.

2xs is for IconButton only. It renders a 20px box and shrinks the icon to 16px, so the button fits inside a line of text, such as a copy button beside a value in a Description List row. The other sizes scale only the box.

Size 2xs

Size xs

Size sm

Size md

Size lg

Size xl

Disabled

import { IconButton } from "@ngrok/mantle/button";import { GlobeIcon } from "@phosphor-icons/react/Globe"; <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="2xs" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="2xs" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="2xs" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="xs" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="xs" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="xs" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="sm" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="sm" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="sm" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="md" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="md" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="md" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="lg" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="lg" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="lg" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" size="xl" icon={<GlobeIcon />} /><IconButton appearance="filled" intent="neutral" label="prestige worldwide" size="xl" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" size="xl" icon={<GlobeIcon />} />

Type

Like Button, IconButton defaults to type="button" rather than the native submit, so it never accidentally submits a surrounding <form>. Opt in with type="submit" (or type="reset") when the button should submit or reset a form. When asChild is used, type has no effect and is not forwarded to the child.

isLoading

isLoading determines whether the icon button is in a loading state, default false. Setting isLoading replaces the icon with a spinner. It also disables user interaction with the button and sets aria-disabled.

idle

isLoading

import { IconButton } from "@ngrok/mantle/button";import { GlobeIcon } from "@phosphor-icons/react/Globe"; <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" icon={<GlobeIcon />} /> <IconButton appearance="ghost" intent="neutral" label="prestige worldwide" isLoading icon={<GlobeIcon />} /><IconButton appearance="outlined" intent="neutral" label="prestige worldwide" isLoading icon={<GlobeIcon />} />

Polymorphism

To render something else as an IconButton, use the asChild prop to compose. This is useful when you want to splat the IconButton styling onto a react-router Link. When you use asChild, the IconButton does NOT pass its type prop to the child component.

A disabled or loading IconButton under asChild gets no disabled attribute, because the attribute is inert on an <a>. The child gets aria-disabled="true", leaves the tab order with tabIndex={-1}, and a click on it is cancelled before any handler or router navigation runs.

import { IconButton } from "@ngrok/mantle/button";import { GlobeIcon } from "@phosphor-icons/react/Globe";import { Link, href } from "react-router"; <IconButton	appearance="outlined"	intent="neutral"	asChild	label="prestige worldwide"	icon={<GlobeIcon />}>	<Link to={href("/base/colors")} /></IconButton>;

API Reference

The IconButton accepts the following props in addition to the standard HTML button attributes.

PropTypeDefaultDescription
appearance"filled" | "ghost" | "outlined"The visual style of the IconButton. Required — there is no default, so every call site states the visual weight it means. Matches Button's union without "link".
asChildbooleanfalseUse the asChild prop to compose the IconButton styling and functionality onto alternative element types or your own React components.
iconReactNodeThe icon to render inside the button. When isLoading is true, the icon is automatically replaced with a spinner.
intent"neutral"The tone of the IconButton — the purpose its color communicates to the user. Narrower than Button's: "neutral" is the only tone IconButton draws, so the accent and danger tones are a type error here. Still required, so a call site reads the same on both components.
isLoadingbooleanfalseDetermines whether the icon button is in a loading state. Setting isLoading replaces the icon with a spinner. It also disables user interaction with the button and sets aria-disabled.
labelstringThe accessible name for the button, rendered as its aria-label (like the alt text on an <img>). Passing aria-label directly is a compile error, so this prop is the only way to name the button. Name the action, not the icon: "Delete API key", not "Trash" or "Trash icon". Keep it short (a voice-control user speaks the label to click the button) and specific ("Delete" alone cannot tell two delete buttons apart), and never include the word "button" (the role already announces it).
size"2xs" | "xs" | "sm" | "md" | "lg" | "xl""md"The size of the IconButton, controlling its box size. Shared scale with Button: the same size name renders the same box height. 2xs is IconButton only, a 20px box with a 16px icon. The other sizes scale only the box.
type"button" | "reset" | "submit""button"The behavior of the IconButton when activated. Defaults to "button", so an IconButton inside a <form> will not submit it — pass type="submit" to submit the form. When asChild is used, type has no effect and is not forwarded to the child.

Data attributes

Data AttributeValueDescription
data-slot"icon-button"On the button element. Stable styling hook that survives a className override or an asChild swap.
data-icon-button"true"Always present. Lets a wrapper style every icon button it contains.
data-appearance"filled" | "ghost" | "outlined"The appearance prop.
data-intent"neutral"The intent prop.
data-size"2xs" | "xs" | "sm" | "md" | "lg" | "xl"The size prop.
data-loadingpresent when isLoadingPresence-only, never "false".
data-disabledpresent when disabledPresence-only, never "false", since the data-disabled: variant matches the attribute's existence. Set when the button resolves disabled: disabled, aria-disabled="true", or isLoading with no explicit disabled. disabled={false} keeps a loading button enabled and omits it.

The IconButton also exposes the following CSS variables as part of its API.

CSS VariableDefaultDescription
--icon-button-border-radius0.375remThe border radius of the IconButton. Wrappers can set it to slot icon buttons into their chrome — ButtonGroup's panel appearance tightens it to 0.125rem.