A button with one icon and no text, in ghost or outline, or as a link.
Variants
Ghost has no fill and no edge. Outline draws a 1px ink edge, for a button that has to show where it is.
Example
Example
'use client';
import { IconButton } from '@snapshot-ui/react';
import { Bell, Search, Settings } from 'lucide-react';
import type { ReactElement } from 'react';
export default function IconButtonVariants(): ReactElement {
return (
<div style={{ display: 'grid', gap: 16 }}>
<div className="row" style={{ justifyContent: 'center', gap: 12 }}>
<IconButton aria-label="Search">
<Search />
</IconButton>
<IconButton aria-label="Notifications">
<Bell />
</IconButton>
<IconButton aria-label="Settings">
<Settings />
</IconButton>
</div>
<div className="row" style={{ justifyContent: 'center', gap: 12 }}>
<IconButton variant="outline" aria-label="Search">
<Search />
</IconButton>
<IconButton variant="outline" aria-label="Notifications">
<Bell />
</IconButton>
<IconButton variant="outline" aria-label="Settings">
<Settings />
</IconButton>
</div>
</div>
);
}States
Hover and press wash the circle with a little ink, in dark as well. Disabled turns the icon taupe and ignores presses.
Example
Example
'use client';
import { IconButton } from '@snapshot-ui/react';
import { Check, Copy } from 'lucide-react';
import { useEffect, useRef, useState, type ReactElement } from 'react';
export default function IconButtonStates(): ReactElement {
const [copied, setCopied] = useState(false);
const timer = useRef(0);
useEffect(
() => () => {
window.clearTimeout(timer.current);
},
[],
);
return (
<div className="row" style={{ justifyContent: 'center', gap: 12 }}>
<IconButton
aria-label="Copy"
onPress={() => {
setCopied(true);
window.clearTimeout(timer.current);
timer.current = window.setTimeout(() => {
setCopied(false);
}, 2000);
}}
>
{copied ? <Check /> : <Copy />}
</IconButton>
<span className="visually-hidden" role="status">
{copied ? 'Copied' : ''}
</span>
<IconButton aria-label="Copy" isDisabled>
<Copy />
</IconButton>
<IconButton variant="outline" aria-label="Copy" isDisabled>
<Copy />
</IconButton>
</div>
);
}As a link
Give it an href to render an anchor with the same look, which still needs a name. Use the link form for an icon that goes somewhere, and the button form for one that does something. The link form follows the app's router, as set up in Routing.
Example
Example
'use client';
import { IconButton } from '@snapshot-ui/react';
import { ExternalLink } from 'lucide-react';
import type { ReactElement } from 'react';
export default function IconButtonLink(): ReactElement {
return (
<div className="row" style={{ justifyContent: 'center', gap: 12 }}>
<IconButton href="https://github.com/amirehsan/snapshot-ui" aria-label="Snapshot UI on GitHub">
<ExternalLink />
</IconButton>
<IconButton
variant="outline"
href="https://github.com/amirehsan/snapshot-ui"
aria-label="Snapshot UI on GitHub"
>
<ExternalLink />
</IconButton>
</div>
);
}Usage
The child is the icon. It is hidden from assistive technology, so the name comes from aria-label or aria-labelledby, and the types require one of them.
import { IconButton } from '@snapshot-ui/react';
import { Settings } from 'lucide-react';
export function SettingsButton({ onOpen }: { onOpen: () => void }) {
return (
<IconButton aria-label="Open settings" onPress={onOpen}>
<Settings />
</IconButton>
);
}Props
Every React Aria Button prop is accepted, or every Link prop when there is an href. These are the ones Snapshot UI adds or changes.
aria-label string | A name for assistive technology. Give it, or |
|---|---|
aria-labelledby string | The id of an element that holds the name, such as a label shown elsewhere. |
href string | A URL to link to. Given an |
onPress ((e: PressEvent) => void) | Handler that is called when the press is released over the target. |
variant IconButtonVariant |
Default: 'ghost' |
Accessibility
A native button element, or an anchor when there is an href, with React Aria handling press across mouse, touch and keyboard. It is 40px square, which meets the touch target on its own.
| SpaceEnter | Activates the button. |
|---|---|
| Enter | Follows the link, when it has an href. |
| Tab | Moves focus to it. The 2px ink ring sits outside the circle. |
Design rules
What keeps icon buttons in the system.
Name every one for its action, such as Open the menu or Copy code.
Use ghost in bars and headers, and outline where the button needs an edge to read as a button.
Never use one for the main action of a view. A Button with a label says what it does.
Never put text in it. If it needs words, use a Button.