Skip to content
Dethink Components

ComponentsIconButton

IconButton

Show an action as an icon when space is limited.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
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
examples/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
examples/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
examples/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.

IconButton props
PropWhat it doesDefault
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"
loadingbooleanSpinner replaces the icon; the button becomes inert.false
…native button propsButtonHTMLAttributesRenders a real button: onClick, disabled, aria-pressed, type.Not set