components / actions
Button
Button with variants, sizes, a loading state, and the option to render as a link.
button-default.tsx
import { Button } from "@/components/ui/button";
export default function ButtonDefault() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button>save</Button>
<Button variant="brand">get started</Button>
<Button variant="outline">cancel</Button>
<Button variant="ghost">dismiss</Button>
<Button variant="destructive">delete</Button>
<Button variant="link">learn more</Button>
</div>
);
}Installation
$ npx shadcn@latest add https://cd-ui.vercel.app/r/button.jsonfirst time? set up components.json and the theme first: see installation.
Usage
import { Button } from "@/components/ui/button"
<Button variant="brand">get started</Button>Examples
Sizes
Three heights and two square icon sizes.
button-sizes.tsx
import { PlusIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function ButtonSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button size="sm">small</Button>
<Button size="md">medium</Button>
<Button size="lg">large</Button>
<Button aria-label="Add" size="icon" variant="outline">
<PlusIcon aria-hidden="true" />
</Button>
</div>
);
}Loading
loading swaps the content for a spinner without changing the button width and blocks repeated clicks.
button-loading.tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
export default function ButtonLoading() {
const [loading, setLoading] = useState(false);
return (
<Button
loading={loading}
onClick={() => {
setLoading(true);
setTimeout(() => setLoading(false), 1500);
}}
variant="brand"
>
publish project
</Button>
);
}Keycap
The key variant looks like a keyboard key: the bottom edge is a shadow that disappears when pressed.
button-key.tsx
import { Button } from "@/components/ui/button";
export default function ButtonKey() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button variant="key">Ctrl</Button>
<Button variant="key">K</Button>
<Button size="sm" variant="key">
Esc
</Button>
<Button size="icon" variant="key">
↵
</Button>
</div>
);
}As a link
With render, the button becomes an <a> (or Next's <Link>) with the same look.
button-link.tsx
import { ArrowUpRightIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function ButtonLink() {
return (
<Button nativeButton={false} render={<a href="https://github.com/di0rio" rel="noopener noreferrer" target="_blank" />} variant="outline">
open on github <ArrowUpRightIcon aria-hidden="true" />
</Button>
);
}API
Button
Base UI · ButtonNative button with variants. Accepts every prop of Base UI's Button.
| prop | type | default |
|---|---|---|
variantVisual style. | "default" | "brand" | "outline" | "ghost" | "link" | "destructive" | "key" | "default" |
sizeHeight and spacing. | "sm" | "md" | "lg" | "icon" | "icon-sm" | "md" |
loadingShows the spinner, keeps the width, sets | boolean | false |
nativeButtonPass | boolean | true |
renderSwaps the rendered element while keeping behavior and style (e.g. become an | ReactElement | (props) => ReactElement | - |
classNameExtra classes, merged with | string | - |
Keyboard
| key | action |
|---|---|
| EnterSpace | Activates the button. |
Accessibility
- Icon-only buttons need an
aria-label. - During
loadingthe button stays focusable (focusableWhenDisabled), so focus doesn't jump elsewhere.