Skip to content

IconButton ​

A button that is an icon and no word. It is a square, and the one place the icon inside it gets its size. Its label is the accessible name and the tooltip at once, so the two never drift apart.

tsx
import { IconButton } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { Bold, ChevronRight, Italic, RefreshCw, Settings, Trash } from 'lucide-react';
import { ButtonGroup, IconButton, Separator, shortcut } from '@basmilius/react-ui';

const REFRESH = shortcut('Mod+R');

export default function IconButtonDemo() {
    const [bold, setBold] = useState(true);
    const [italic, setItalic] = useState(false);
    const [refreshing, setRefreshing] = useState(false);
    const [open, setOpen] = useState(false);

    const refresh = (): void => {
        setRefreshing(true);
        window.setTimeout(() => setRefreshing(false), 1200);
    };

    return (
        <div className="flex items-center gap-2">
            <ButtonGroup>
                <IconButton icon={Bold} label="Bold" aria-pressed={bold} onClick={() => setBold(!bold)} />
                <IconButton icon={Italic} label="Italic" aria-pressed={italic} onClick={() => setItalic(!italic)} />
            </ButtonGroup>
            <Separator />
            <IconButton icon={RefreshCw} label="Refresh" kbd={REFRESH} spin={refreshing} onClick={refresh} />
            <IconButton
                icon={ChevronRight}
                label={open ? 'Collapse' : 'Expand'}
                aria-expanded={open}
                iconClassName={open ? 'rotate-90 transition-transform' : 'transition-transform'}
                onClick={() => setOpen(!open)}
            />
            <IconButton icon={Settings} label="Settings" tooltipSide="bottom" />
            <IconButton icon={Trash} label="Delete" tooltip="Nothing is selected" aria-disabled />
        </div>
    );
}

Sizes ​

The size decides the square, the radius and the icon inside: 16 in the default 32, 14 in 28 (sm), and 12 in 24 (xs) and 20 (2xs). Never set a height, a width or a radius on it. w-auto is the one way to widen it, for a word beside the icon handed in as children.

tsx
import { Plus } from 'lucide-react';
import { IconButton } from '@basmilius/react-ui';

export default function IconButtonSizes() {
    return (
        <div className="flex items-center gap-3">
            <IconButton icon={Plus} label="Add (32, icon 16)" />
            <IconButton icon={Plus} label="Add (28, icon 14)" size="sm" />
            <IconButton icon={Plus} label="Add (24, icon 12)" size="xs" />
            <IconButton icon={Plus} label="Add (20, icon 12)" size="2xs" />
            <IconButton icon={Plus} label="Add a file" className="w-auto gap-1.5 px-2 text-xs">
                Add
            </IconButton>
        </div>
    );
}

Toggles and state ​

active draws the button as a pressed key, for a toggle that is on. Where the button is a real toggle, use aria-pressed instead, as the bold and italic buttons above do. The theme draws both the same. spin turns the icon for a step that is running.

iconClassName goes to the icon rather than the button, for a chevron that turns when its section opens, a status icon in its own tone or a filled glyph (fill-current). The icon keeps the size of the button; one that needs another size is handed in as children without icon.

A disabled icon button with disabled loses its tooltip, because a disabled button takes no pointer events. With aria-disabled it keeps its focus and its tooltip, which can say why nothing happens.

As another element ​

render makes another element the button, such as a menu trigger. The trigger's behavior and the button's look merge into one element:

tsx
<Menu.Root>
    <IconButton icon={Ellipsis} label="More" render={<Menu.Trigger />} />
    <Menu.Popup>...</Menu.Popup>
</Menu.Root>

Props ​

IconButton takes every prop of a <button> except children, plus:

PropTypeDefault
labelstringRequired. The accessible name, and the tooltip unless tooltip says otherwise.
iconLucideIconLeft out for a button that draws something else, handed in as children.
tooltipReactNode | falselabelA tooltip that says more than the name, or false for none.
kbdShortcut | stringThe shortcut printed in the tooltip, and under the button while the modifier is held.
tooltipSideTooltipSide'top'
sizeIconButtonSize'md''md' | 'sm' | 'xs' | '2xs'
activebooleanfalseDraws a pressed key.
spinbooleanfalseTurns the icon.
iconClassNamestringClasses for the icon: a tone, a turn, a fill.
childrenReactNodeDrawn after the icon: a count, or a word with w-auto.
renderRenderProp<button type="button" />Another element to be the button.

IconButtonProps and IconButtonSize are exported types.