Button
Starts an action. Six styles, four sizes, and it can render as a link.
View code
"use client";
import { Button } from "@/components/kit/button";
import { useState } from "react";
function Component() {
const [open, setOpen] = useState<boolean>(false);
return (
<Button
className="group"
variant="outline"
size="icon"
onClick={() => setOpen((prevState) => !prevState)}
aria-expanded={open}
aria-label={open ? "Close menu" : "Open menu"}
>
<svg
className="pointer-events-none"
width={16}
height={16}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 12L20 12"
className="origin-center -translate-y-[7px] transition-all duration-300 [transition-timing-function:cubic-bezier(.5,.85,.25,1.1)] group-aria-expanded:translate-x-0 group-aria-expanded:translate-y-0 group-aria-expanded:rotate-[315deg]"
/>
<path
d="M4 12H20"
className="origin-center transition-all duration-300 [transition-timing-function:cubic-bezier(.5,.85,.25,1.8)] group-aria-expanded:rotate-45"
/>
<path
d="M4 12H20"
className="origin-center translate-y-[7px] transition-all duration-300 [transition-timing-function:cubic-bezier(.5,.85,.25,1.1)] group-aria-expanded:translate-y-0 group-aria-expanded:rotate-[135deg]"
/>
</svg>
</Button>
);
}
export { Component };
Built on Base UI Button. It handles keyboard focus and disabled behavior for you, and it can take the form of a link without losing its styles.
Installation
bunx --bun shadcn@latest add HarshitMahajan/UI/buttonCopies the code to components/kit/button.tsx installs its packages, and adds the tokens it needs to your globals.css. Your existing shadcn components aren't touched.
Usage
import { Button } from "@/components/kit/button"<Button variant="outline">Export</Button>Composition
ButtonFeatures
- Six styles: default, secondary, outline, ghost, link and destructive
- Four sizes, including a square one for icons
- A focus ring that appears for keyboard users only
- Renders as a link or your router's Link through the render prop
- Exported ButtonProps, ButtonVariant and ButtonSize types
Styles
Set variant to change how much attention the button draws.
View code
import { Button } from "@/components/kit/button";
export function ButtonVariants() {
return (
<>
<Button>Save changes</Button>
<Button variant="secondary">Duplicate</Button>
<Button variant="outline">Export</Button>
<Button variant="ghost">Skip</Button>
<Button variant="link">View details</Button>
<Button variant="destructive">Delete</Button>
</>
);
}
Sizes
Use size for the height and padding. An icon button also needs an aria-label.
View code
import { Plus } from "lucide-react";
import { Button } from "@/components/kit/button";
export function ButtonSizes() {
return (
<>
<Button size="sm">Small</Button>
<Button>Default</Button>
<Button size="lg">Large</Button>
<Button size="icon" variant="outline" aria-label="Add item">
<Plus size={16} strokeWidth={2} aria-hidden="true" />
</Button>
</>
);
}
With an icon
Place an icon before or after the text. Icons never shrink and never catch clicks.
View code
import { Archive, Download } from "lucide-react";
import { Button } from "@/components/kit/button";
export function ButtonWithIcon() {
return (
<>
<Button>
<Archive className="-ms-1 me-2 opacity-60" size={16} strokeWidth={2} aria-hidden="true" />
Archive
</Button>
<Button variant="outline">
Download
<Download className="-me-1 ms-2 opacity-60" size={16} strokeWidth={2} aria-hidden="true" />
</Button>
</>
);
}
Loading
Disable the button and show a spinning icon while work is in progress.
View code
import { LoaderCircle } from "lucide-react";
import { Button } from "@/components/kit/button";
export function ButtonLoading() {
return (
<Button disabled>
<LoaderCircle className="-ms-1 me-2 animate-spin" size={16} aria-hidden="true" />
Saving
</Button>
);
}
Disabled
Disabled buttons drop to half opacity and ignore the pointer.
View code
import { Button } from "@/components/kit/button";
export function ButtonDisabled() {
return (
<>
<Button disabled>Save changes</Button>
<Button disabled variant="outline">
Export
</Button>
</>
);
}
As a link
Pass render with nativeButton={false} to get a real link with button styles.
View code
import { Button } from "@/components/kit/button";
export function ButtonAsLink() {
return (
<Button render={<a href="#install" />} nativeButton={false} variant="outline">
Go to install
</Button>
);
}
Accessibility
Label icon-only buttons
A button with only an icon has no text to announce, so give it an aria-label that says what it does.
<Button size="icon" aria-label="Add item">
<Plus aria-hidden="true" />
</Button>Use a link for navigation
If clicking takes people to another page, render a link so they can open it in a new tab and screen readers announce it correctly.
API Reference
Button
Accepts everything Base UI Button accepts.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "outline" | "ghost" | "link" | "destructive" | "default" | The visual style. |
| size | "default" | "sm" | "lg" | "icon" | "default" | The height and padding. |
| render | ReactElement | function | - | Render the button as another element, such as a link or your router's Link. |
| nativeButton | boolean | true | Set to false when render is not a button element. |
| disabled | boolean | false | Stops the button from being used. |
| focusableWhenDisabled | boolean | false | Keeps a disabled button reachable by keyboard, such as while it loads. |
| className | string | - | Additional classes for the button. |