Icon Button
Square icon-only button in two sizes.
Sizes
Square icon-only buttons in two sizes.
function IconButtonSizes() {
return (
<div className="flex flex-wrap items-center gap-3">
<IconButton icon={RiSettings3Line} size="medium" aria-label="Settings" />
<IconButton icon={RiMore2Line} size="small" aria-label="More" />
</div>
);
}function IconButtonSizes() {
return (
<div className="flex flex-wrap items-center gap-3">
<IconButton icon={RiSettings3Line} size="medium" aria-label="Settings" />
<IconButton icon={RiMore2Line} size="small" aria-label="More" />
</div>
);
}Installation
npx shadcn@latest add https://boardcn.dev/r/icon-button.jsonnpx shadcn@latest add https://boardcn.dev/r/icon-button.jsonBoardCN dependencies
The CLI installs these for you — you do not need to add them yourself.
Source
The file the CLI copies into your project.
import type {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ComponentType,
Ref,
} from "react";
import { cx, sortCx } from "@/utils/cx";
type IconButtonSize = "medium" | "small";
type IconComponent = ComponentType<{
className?: string;
"aria-hidden"?: boolean | "true" | "false";
}>;
export interface IconButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
icon: IconComponent;
size?: IconButtonSize;
/** Accessible name — required since there is no visible label. */
"aria-label": string;
ref?: Ref<HTMLButtonElement>;
}
export interface IconLinkButtonProps
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "children"> {
icon: IconComponent;
size?: IconButtonSize;
/** Accessible name — required since there is no visible label. */
"aria-label": string;
ref?: Ref<HTMLAnchorElement>;
}
const styles = sortCx({
base: [
"relative inline-flex shrink-0 items-center justify-center overflow-visible rounded-2lg",
"bg-background-primary-default text-foreground-icon-primary",
"border border-border-button-default shadow-xs",
"select-none cursor-pointer",
"transition-[background-color,border-color,box-shadow,color] duration-150 ease",
"outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-border-focus-ring",
"hover:bg-background-primary-hover hover:border-border-button-hover",
"active:bg-background-primary-active active:border-border-button-active",
"disabled:cursor-not-allowed disabled:bg-background-primary-disabled disabled:border-border-button-default disabled:text-icon-button-disabled-foreground disabled:opacity-60 disabled:shadow-none",
].join(" "),
size: {
medium: "size-9",
small: "size-8",
},
icon: {
medium: "size-5 shrink-0",
small: "size-4 shrink-0",
},
});
export function IconButton({
icon: Icon,
size = "medium",
className,
type = "button",
ref,
...props
}: IconButtonProps) {
return (
<button
ref={ref}
type={type}
className={cx(styles.base, styles.size[size], className)}
{...props}
>
<Icon className={styles.icon[size]} aria-hidden />
</button>
);
}
/** Anchor counterpart to IconButton for external and navigational actions. */
export function IconLinkButton({
icon: Icon,
size = "medium",
className,
ref,
...props
}: IconLinkButtonProps) {
return (
<a
ref={ref}
className={cx(styles.base, styles.size[size], className)}
{...props}
>
<Icon className={styles.icon[size]} aria-hidden />
</a>
);
}import type {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ComponentType,
Ref,
} from "react";
import { cx, sortCx } from "@/utils/cx";
type IconButtonSize = "medium" | "small";
type IconComponent = ComponentType<{
className?: string;
"aria-hidden"?: boolean | "true" | "false";
}>;
export interface IconButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
icon: IconComponent;
size?: IconButtonSize;
/** Accessible name — required since there is no visible label. */
"aria-label": string;
ref?: Ref<HTMLButtonElement>;
}
export interface IconLinkButtonProps
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "children"> {
icon: IconComponent;
size?: IconButtonSize;
/** Accessible name — required since there is no visible label. */
"aria-label": string;
ref?: Ref<HTMLAnchorElement>;
}
const styles = sortCx({
base: [
"relative inline-flex shrink-0 items-center justify-center overflow-visible rounded-2lg",
"bg-background-primary-default text-foreground-icon-primary",
"border border-border-button-default shadow-xs",
"select-none cursor-pointer",
"transition-[background-color,border-color,box-shadow,color] duration-150 ease",
"outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-border-focus-ring",
"hover:bg-background-primary-hover hover:border-border-button-hover",
"active:bg-background-primary-active active:border-border-button-active",
"disabled:cursor-not-allowed disabled:bg-background-primary-disabled disabled:border-border-button-default disabled:text-icon-button-disabled-foreground disabled:opacity-60 disabled:shadow-none",
].join(" "),
size: {
medium: "size-9",
small: "size-8",
},
icon: {
medium: "size-5 shrink-0",
small: "size-4 shrink-0",
},
});
export function IconButton({
icon: Icon,
size = "medium",
className,
type = "button",
ref,
...props
}: IconButtonProps) {
return (
<button
ref={ref}
type={type}
className={cx(styles.base, styles.size[size], className)}
{...props}
>
<Icon className={styles.icon[size]} aria-hidden />
</button>
);
}
/** Anchor counterpart to IconButton for external and navigational actions. */
export function IconLinkButton({
icon: Icon,
size = "medium",
className,
ref,
...props
}: IconLinkButtonProps) {
return (
<a
ref={ref}
className={cx(styles.base, styles.size[size], className)}
{...props}
>
<Icon className={styles.icon[size]} aria-hidden />
</a>
);
}Props
Generated from the component's TypeScript types. Standard DOM and React Aria props are omitted.
IconButton
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-labelrequired | string | — | Accessible name — required since there is no visible label. |
| iconrequired | IconComponent | — | — |
| size | "medium" | "small" | medium | — |
IconLinkButton
Anchor counterpart to IconButton for external and navigational actions.
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-labelrequired | string | — | Accessible name — required since there is no visible label. |
| iconrequired | IconComponent | — | — |
| size | "medium" | "small" | medium | — |