UI.MD

Button

An interactive button with multiple variants, sizes, icons, loading state, and Motion layout animation.

Installation

$
npx shadcn@latest add https://ui.mattdowney.com/r/button.json

Usage

import { Button } from "@/components/ui/button"

Examples

Default

Variants

Seven fills. soft is the accent-tinted secondary that pairs with default, and subtle is its neutral version for toolbars. link paints no fill, so it ignores the size's height and sits on the text line.

Sizes

Four heights (24/28/32/40) plus three square icon sizes. An Input given the same size matches the button beside it in height and corner.

With Icon

Pass an element: it works anywhere, including from a Server Component. A bare component reference (iconStart={Mail}) also works, but only from a Client Component, because a function cannot cross the server/client boundary.

Icon sizes

Pass iconStart or iconEnd and the icon sizes itself (12/14/14/16px across the four heights), with its side padding adjusted to match. Set iconSize only to override it.

With count

Pass count to add a count chip inside the button. It stays readable on every variant and through hover.

Loading

loading swaps the label for a spinner, blocks the click, and sets aria-busy. The box does not resize, so a row of buttons holds its layout. Each variant keeps its own hover fill while busy, so a loading button reads as the same button mid-press rather than turning into a different one.

API Reference

PropTypeDefaultDescription
variant"default" | "soft" | "outline" | "ghost" | "destructive" | "subtle" | "link""default"Visual style of the button. soft is the accent-tinted secondary that pairs with default; subtle is its neutral equivalent for toolbars. link paints no fill, so it ignores the size's height and padding and sits on the text line at every size.
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-sm" | "icon-lg" | "inline""default"Height and padding preset. icon (32px), icon-sm (24px), and icon-lg (40px) are square; inline flows with surrounding text. variant="link" takes only the type scale from this, never the height or padding.
iconStartTablerIcon | ReactNode-Icon rendered before the label. Prefer an element (<Mail />); a bare component (Mail) still works but cannot be passed from a Server Component.
iconEndTablerIcon | ReactNode-Icon rendered after the label. Prefer an element (<ChevronRight />); a bare component still works but cannot be passed from a Server Component.
iconSize"xs" | "sm" | "default" | "lg" | "xl"-Overrides the icon size implied by size.
countReact.ReactNode-Count chip rendered after the label, tinted to match the variant.
countClassNamestring-Extra classes for the count chip.
loadingbooleanfalseShows a centered spinner, locks in the variant's hover styling, and blocks clicks while preserving the button's width.
layoutAnimationbooleanfalseMotion layout animation on content changes. Set false to prevent glitches on rapid state changes; disabled automatically under reduced motion.
asChildbooleanfalseRenders the child element instead of a <button>, merging classes and injecting icons, count, and spinner into it.