ComponentsIconButton
IconButton
Show an action as an icon when space is limited.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry filesImplemented with React and browser APIs, without an additional behavior or rendering library.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import { IconButton } from "@dethink/components";Try the examples, then open the code to use them in your app.
Variants and shapes
The Button variants minus link, plus a circle shape.
Show sourceexamples/icon-button/basic.tsx
"use client";
import { IconButton } from "@dethink/components";
import { Bell, Bookmark, Search, Settings, Trash2 } from "lucide-react";
export function IconButtonBasic() {
return (
<div className="flex flex-wrap items-center justify-center gap-2">
<IconButton aria-label="Search">
<Search />
</IconButton>
<IconButton aria-label="Notifications" variant="soft">
<Bell />
</IconButton>
<IconButton aria-label="Settings" variant="outline">
<Settings />
</IconButton>
<IconButton aria-label="Bookmark" variant="ghost">
<Bookmark />
</IconButton>
<IconButton aria-label="Delete" variant="destructive">
<Trash2 />
</IconButton>
<IconButton
aria-label="Search everywhere"
shape="circle"
variant="outline"
>
<Search />
</IconButton>
</div>
);
}Sizes, loading, and disabled
Five sizes with auto-scaled icons; loading swaps the icon for a spinner and makes the button inert.
Show sourceexamples/icon-button/states.tsx
"use client";
import { IconButton } from "@dethink/components";
import { RefreshCw, Upload } from "lucide-react";
const sizes = ["xs", "sm", "md", "lg", "xl"] as const;
export function IconButtonStates() {
return (
<div className="space-y-4">
<div className="flex items-center justify-center gap-2">
{sizes.map((size) => (
<IconButton
key={size}
aria-label={`Upload (${size})`}
size={size}
variant="soft"
>
<Upload />
</IconButton>
))}
</div>
<div className="flex items-center justify-center gap-2">
<IconButton aria-label="Refreshing" loading variant="outline">
<RefreshCw />
</IconButton>
<IconButton aria-label="Refresh" disabled variant="outline">
<RefreshCw />
</IconButton>
</div>
</div>
);
}Examples that combine components for common tasks.
Player bar
Toggle buttons done right: the Play button's accessible name flips with its state, likes and repeat carry aria-pressed, and a live region narrates the state for screen readers.
Paused
Show sourceexamples/icon-button/recipe-player.tsx
"use client";
import { useState } from "react";
import { IconButton } from "@dethink/components";
import {
Heart,
Pause,
Play,
Repeat,
SkipBack,
SkipForward,
} from "lucide-react";
/**
* A player bar built entirely from icon buttons: the accessible name flips
* with the toggle state (Play/Pause), aria-pressed carries the toggles, and
* a live region narrates state for screen-reader users.
*/
export function IconButtonRecipePlayer() {
const [playing, setPlaying] = useState(false);
const [liked, setLiked] = useState(false);
const [repeat, setRepeat] = useState(false);
return (
<div className="mx-auto max-w-xs space-y-3">
<div className="border-border bg-muted/40 flex items-center justify-center gap-1 rounded-full border px-3 py-2">
<IconButton aria-label="Previous track" variant="ghost" shape="circle">
<SkipBack />
</IconButton>
<IconButton
aria-label={playing ? "Pause" : "Play"}
shape="circle"
size="lg"
onClick={() => setPlaying(!playing)}
>
{playing ? <Pause /> : <Play />}
</IconButton>
<IconButton aria-label="Next track" variant="ghost" shape="circle">
<SkipForward />
</IconButton>
<IconButton
aria-label="Like"
aria-pressed={liked}
variant={liked ? "soft" : "ghost"}
shape="circle"
onClick={() => setLiked(!liked)}
>
<Heart />
</IconButton>
<IconButton
aria-label="Repeat"
aria-pressed={repeat}
variant={repeat ? "soft" : "ghost"}
shape="circle"
onClick={() => setRepeat(!repeat)}
>
<Repeat />
</IconButton>
</div>
<p
aria-live="polite"
className="text-muted-foreground text-center text-sm"
>
{playing ? "Playing" : "Paused"}
{liked ? " · liked" : ""}
{repeat ? " · repeat on" : ""}
</p>
</div>
);
}IconButton renders a real button element.
| Prop | What it does | Default |
|---|---|---|
aria-label / aria-labelledbystring (one required) | The accessible name is required by the type system — an icon-only button without one will not compile. | Not set |
variant"solid" | "soft" | "outline" | "ghost" | "destructive" | Visual treatment matching the Button variants. | "solid" |
size"xs" | "sm" | "md" | "lg" | "xl" | Square control size; the icon scales with it. | "md" |
shape"square" | "circle" | Corner treatment. | "square" |
loadingboolean | Spinner replaces the icon; the button becomes inert. | false |
…native button propsButtonHTMLAttributes | Renders a real button: onClick, disabled, aria-pressed, type. | Not set |