Button
Displays a button or a component that looks like a button.
Installation
pnpm dlx shadcn@latest add @lumi-ui/button
Basic Usage
import { Button } from "@/components/ui/button"<Button>Button</Button>Variants
Default
Outline
Secondary
Ghost
Destructive
Link
Size
Rounded
Use the rounded-full class to make the button rounded.
With Icon
The spacing between the icon and the text is automatically adjusted based on the size of the button. You do not need any margin on the icon.
Cookbook
Rendering as another tag
Base UI uses the render prop to change the underlying DOM element while preserving accessibility and behavior. This replaces the asChild pattern found in Radix UI.
<Button render={<div />} nativeButton={false}>
Button that can contain complex children
</Button>Here's an example rendering a link that looks like a button.
import Link from "next/link"
import { Button } from "@/components/ui/button"
export function ButtonLink() {
return (
<Button render={<Link href="/login" />} nativeButton={false}>
Login
</Button>
)
}Be sure to set nativeButton={false} to ensure proper accessibility semantics if you are rendering a non-button element.
Loading State
The isLoading prop automatically disables the button and prevents interaction.
Unlike a standard disabled attribute, isLoading preserves focus on the button (via focusableWhenDisabled). This prevents focus from being lost to the body during async form submissions, maintaining a smooth keyboard navigation flow.
Cursor
Tailwind v4 switched from cursor: pointer to cursor: default for the button component.
If you want to keep the cursor: pointer behavior, add the following code to your CSS file:
@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}API Reference
Migration Example
import Link from "next/link"
import {
Button,
} from "@/components/ui/button"
function ButtonDemo() {
return (
<Button asChild>
<Link href="/login">Login</Link>
</Button>
);
}