Button
A flexible button component that supports multiple variants, sizes, and states. Built with Ark UI factory for accessibility and styled using Ocean UI design tokens.
Example
import { Button } from "@/components/ui/button";
export default function ButtonDemo() {
return (
<div className="flex flex-wrap gap-4">
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonDemo() {
return (
<div class="flex flex-wrap gap-4">
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
</div>
);
}
How to install and use
Complete the manual installation setup
If you haven't already completed the first 4 steps of the manual installation guide, please do so before continuing to install these components.
Create a button.tsx file and paste the following code into it.
import { cva, type VariantProps } from "class-variance-authority";
import { ark } from "@ark-ui/react/factory";
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-semibold transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
{
variants: {
variant: {
primary:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
tertiary: "text-muted-foreground hover:bg-muted hover:text-foreground",
outline:
"border border-border bg-transparent text-foreground hover:bg-muted",
ghost: "text-foreground hover:bg-muted",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
sm: "h-8 px-3 py-2 has-[>svg]:px-2.5",
md: "h-9 px-3.5 py-2.5 has-[>svg]:px-3",
lg: "h-10 px-4 py-2.5 has-[>svg]:px-3.5",
xl: "h-11 px-4.5 py-3 has-[>svg]:px-4",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
);
export interface ButtonProps
extends ComponentProps<typeof ark.button>,
VariantProps<typeof buttonVariants> {}
function Button({
variant,
size,
className,
...props
}: ButtonProps) {
return (
<ark.button
type="button"
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };
import { cva, type VariantProps } from "class-variance-authority";
import { ark } from "@ark-ui/solid/factory";
import type { ComponentProps, ParentComponent } from "solid-js";
import { splitProps } from "solid-js";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-semibold transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
{
variants: {
variant: {
primary:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
tertiary: "text-muted-foreground hover:bg-muted hover:text-foreground",
outline:
"border border-border bg-transparent text-foreground hover:bg-muted",
ghost: "text-foreground hover:bg-muted",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
sm: "h-8 px-3 py-2 has-[>svg]:px-2.5",
md: "h-9 px-3.5 py-2.5 has-[>svg]:px-3",
lg: "h-10 px-4 py-2.5 has-[>svg]:px-3.5",
xl: "h-11 px-4.5 py-3 has-[>svg]:px-4",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
);
export interface ButtonProps
extends ComponentProps<typeof ark.button>,
VariantProps<typeof buttonVariants> {}
export const Button: ParentComponent<ButtonProps> = (props) => {
const [local, rest] = splitProps(props, ["variant", "size", "class"]);
return (
<ark.button
type="button"
data-slot="button"
class={cn(buttonVariants({ variant: local.variant, size: local.size }), local.class)}
{...rest}
/>
);
};
export { buttonVariants };
import { cva, type VariantProps } from "class-variance-authority";
import { ark } from "@ark-ui/react/factory";
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-semibold transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
{
variants: {
variant: {
primary:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
tertiary: "text-muted-foreground hover:bg-muted hover:text-foreground",
outline:
"border border-border bg-transparent text-foreground hover:bg-muted",
ghost: "text-foreground hover:bg-muted",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
sm: "h-8 px-3 py-2 has-[>svg]:px-2.5",
md: "h-9 px-3.5 py-2.5 has-[>svg]:px-3",
lg: "h-10 px-4 py-2.5 has-[>svg]:px-3.5",
xl: "h-11 px-4.5 py-3 has-[>svg]:px-4",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
);
export interface ButtonProps
extends ComponentProps<typeof ark.button>,
VariantProps<typeof buttonVariants> {}
function Button({
variant,
size,
className,
...props
}: ButtonProps) {
return (
<ark.button
type="button"
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };
import { cva, type VariantProps } from "class-variance-authority";
import { ark } from "@ark-ui/react/factory";
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-semibold transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
{
variants: {
variant: {
primary:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
tertiary: "text-muted-foreground hover:bg-muted hover:text-foreground",
outline:
"border border-border bg-transparent text-foreground hover:bg-muted",
ghost: "text-foreground hover:bg-muted",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
sm: "h-8 px-3 py-2 has-[>svg]:px-2.5",
md: "h-9 px-3.5 py-2.5 has-[>svg]:px-3",
lg: "h-10 px-4 py-2.5 has-[>svg]:px-3.5",
xl: "h-11 px-4.5 py-3 has-[>svg]:px-4",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
);
export interface ButtonProps
extends ComponentProps<typeof ark.button>,
VariantProps<typeof buttonVariants> {}
function Button({
variant,
size,
className,
...props
}: ButtonProps) {
return (
<ark.button
type="button"
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };
Finally, Choose any example you like and add it to your project.
For instance, create a new file at components/shared/{example-component-name}.tsx, paste the example code into that file, and then import and use the component wherever you need it in your application.
The Button component uses Ark UI's factory function (ark.button) for
built-in accessibility. It's styled with Tailwind CSS using Ocean UI design
tokens from tokens.css.
Overview
The Button component is a fundamental UI element for triggering actions. It supports multiple visual styles, sizes, loading states, and icon placement. Built on top of Ark UI's factory function, it provides native button accessibility out of the box.
Variants
The Button component supports seven visual variants:
- primary: Main action button with brand color background
- secondary: Secondary action with subtle background and border
- tertiary: Tertiary action with tertiary text color and hover background
- outline: Outlined button with transparent background
- ghost: Minimal button with no background, only text
- destructive: For destructive actions (delete, remove)
- link: Text button styled as a link
Sizes
The Button component supports four sizes:
- sm: Small button (height: 32px)
- md: Medium button (height: 36px) - default
- lg: Large button (height: 40px)
- xl: Extra large button (height: 44px)
Features
- Icons: Support for leading and trailing icons
- Loading State: Built-in loading spinner with optional loading text
- Disabled State: Proper disabled styling and behavior
- Accessibility: Full keyboard navigation and ARIA support via Ark UI
- Customizable: All styles can be overridden via
classNameprop
Select Examples
Primary
Primary variant buttons are used for main actions. They feature a brand color background and are the most prominent buttons in the interface.
import { Button } from "@/components/ui/button";
export default function ButtonPrimary() {
return (
<div className="flex gap-4">
<Button size="sm">Primary sm</Button>
<Button size="md">Primary md</Button>
<Button size="lg">Primary lg</Button>
<Button size="xl"> Primary xl</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonPrimary() {
return (
<div class="flex gap-4">
<Button size="sm">Primary sm</Button>
<Button size="md">Primary md</Button>
<Button size="lg">Primary lg</Button>
<Button size="xl">Primary xl</Button>
</div>
);
}
Secondary
Secondary variant buttons are used for secondary actions. They have a subtle background and border, providing a less prominent alternative to primary buttons.
import { Button } from "@/components/ui/button";
export default function ButtonSecondary() {
return (
<div className="flex gap-4">
<Button variant="secondary" size="sm">
Secondary sm
</Button>
<Button variant="secondary" size="md">
Secondary md
</Button>
<Button variant="secondary" size="lg">
Secondary lg
</Button>
<Button variant="secondary" size="xl">
Secondary xl
</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonSecondary() {
return (
<div class="flex gap-4">
<Button variant="secondary" size="sm">
Secondary sm
</Button>
<Button variant="secondary" size="md">
Secondary md
</Button>
<Button variant="secondary" size="lg">
Secondary lg
</Button>
<Button variant="secondary" size="xl">
Secondary xl
</Button>
</div>
);
}
Tertiary
Tertiary variant buttons use tertiary text colors and show a background on hover. They're useful for less prominent actions that still need to be visible.
import { Button } from "@/components/ui/button";
export default function ButtonTertiary() {
return (
<div className="flex gap-4">
<Button variant="tertiary" size="sm">Tertiary sm</Button>
<Button variant="tertiary" size="md">Tertiary md</Button>
<Button variant="tertiary" size="lg">Tertiary lg</Button>
<Button variant="tertiary" size="xl">Tertiary xl</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonTertiary() {
return (
<div class="flex gap-4">
<Button variant="tertiary" size="sm">Tertiary sm</Button>
<Button variant="tertiary" size="md">Tertiary md</Button>
<Button variant="tertiary" size="lg">Tertiary lg</Button>
<Button variant="tertiary" size="xl">Tertiary xl</Button>
</div>
);
}
Outline
Outline variant buttons have a transparent background with a border. They're useful for actions that need to be visible but less prominent than primary or secondary buttons.
import { Button } from "@/components/ui/button";
export default function ButtonOutline() {
return (
<div className="flex gap-4">
<Button variant="outline" size="sm">Outline sm</Button>
<Button variant="outline" size="md">Outline md</Button>
<Button variant="outline" size="lg">Outline lg</Button>
<Button variant="outline" size="xl">Outline xl</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonOutline() {
return (
<div class="flex gap-4">
<Button variant="outline" size="sm">Outline sm</Button>
<Button variant="outline" size="md">Outline md</Button>
<Button variant="outline" size="lg">Outline lg</Button>
<Button variant="outline" size="xl">Outline xl</Button>
</div>
);
}
Ghost
Ghost variant buttons have no background and minimal styling. They're ideal for subtle actions or when you want the button to blend into the interface.
import { Button } from "@/components/ui/button";
export default function ButtonGhost() {
return (
<div className="flex gap-4">
<Button variant="ghost" size="sm">Ghost sm</Button>
<Button variant="ghost" size="md">Ghost md</Button>
<Button variant="ghost" size="lg">Ghost lg</Button>
<Button variant="ghost" size="xl">Ghost xl</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonGhost() {
return (
<div class="flex gap-4">
<Button variant="ghost" size="sm">Ghost sm</Button>
<Button variant="ghost" size="md">Ghost md</Button>
<Button variant="ghost" size="lg">Ghost lg</Button>
<Button variant="ghost" size="xl">Ghost xl</Button>
</div>
);
}
Destructive
Destructive variant buttons are used for dangerous or irreversible actions like deleting or removing content. They use error colors to indicate the severity of the action.
Primary
Outline
Tertiary
import { Button } from "@/components/ui/button";
export default function ButtonDestructive() {
return (
<div className="flex flex-col gap-8">
{/* Primary Destructive */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Primary</h3>
<div className="flex gap-4">
<Button variant="destructive" size="sm">
Button sm
</Button>
<Button variant="destructive" size="md">
Button md
</Button>
<Button variant="destructive" size="lg">
Button lg
</Button>
<Button variant="destructive" size="xl">
Button xl
</Button>
</div>
</div>
{/* Outline Destructive */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Outline</h3>
<div className="flex gap-4">
<Button
variant="outline"
size="sm"
className="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button sm
</Button>
<Button
variant="outline"
size="md"
className="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button md
</Button>
<Button
variant="outline"
size="lg"
className="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button lg
</Button>
<Button
variant="outline"
size="xl"
className="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button xl
</Button>
</div>
</div>
{/* Tertiary Destructive */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Tertiary</h3>
<div className="flex gap-4">
<Button
variant="tertiary"
size="sm"
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button sm
</Button>
<Button
variant="tertiary"
size="md"
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button md
</Button>
<Button
variant="tertiary"
size="lg"
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button lg
</Button>
<Button
variant="tertiary"
size="xl"
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button xl
</Button>
</div>
</div>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonDestructive() {
return (
<div class="flex flex-col gap-8">
{/* Primary Destructive */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Primary</h3>
<div class="flex gap-4">
<Button variant="destructive" size="sm">
Button sm
</Button>
<Button variant="destructive" size="md">
Button md
</Button>
<Button variant="destructive" size="lg">
Button lg
</Button>
<Button variant="destructive" size="xl">
Button xl
</Button>
</div>
</div>
{/* Outline Destructive */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Outline</h3>
<div class="flex gap-4">
<Button
variant="outline"
size="sm"
class="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button sm
</Button>
<Button
variant="outline"
size="md"
class="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button md
</Button>
<Button
variant="outline"
size="lg"
class="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button lg
</Button>
<Button
variant="outline"
size="xl"
class="border-destructive text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button xl
</Button>
</div>
</div>
{/* Tertiary Destructive */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Tertiary</h3>
<div class="flex gap-4">
<Button
variant="tertiary"
size="sm"
class="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button sm
</Button>
<Button
variant="tertiary"
size="md"
class="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button md
</Button>
<Button
variant="tertiary"
size="lg"
class="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button lg
</Button>
<Button
variant="tertiary"
size="xl"
class="text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Button xl
</Button>
</div>
</div>
</div>
);
}
Link
Link variant buttons are styled as text links with underline on hover. They're useful for navigation or less prominent actions.
import { Button } from "@/components/ui/button";
export default function ButtonLink() {
return (
<div className="flex gap-4">
<Button variant="link" size="sm">Link sm</Button>
<Button variant="link" size="md">Link md</Button>
<Button variant="link" size="lg">Link lg</Button>
<Button variant="link" size="xl">Link xl</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonLink() {
return (
<div class="flex gap-4">
<Button variant="link" size="sm">Link sm</Button>
<Button variant="link" size="md">Link md</Button>
<Button variant="link" size="lg">Link lg</Button>
<Button variant="link" size="xl">Link xl</Button>
</div>
);
}
Icon Leading
Buttons with leading icons display icons before the text. This pattern is useful for emphasizing the action type and improving visual scanning.
Primary
Outline
Ghost
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-react";
export default function ButtonIconLeading() {
return (
<div className="flex flex-col gap-8">
{/* Primary with Icon Leading */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Primary</h3>
<div className="flex gap-4">
<Button size="sm">
<Circle className="size-5" />
Button sm
</Button>
<Button size="md">
<Circle className="size-5" />
Button md
</Button>
<Button size="lg">
<Circle className="size-5" />
Button lg
</Button>
<Button size="xl">
<Circle className="size-5" />
Button xl
</Button>
</div>
</div>
{/* Outline with Icon Leading */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Outline</h3>
<div className="flex gap-4">
<Button variant="outline" size="sm">
<Circle className="size-5" />
Button sm
</Button>
<Button variant="outline" size="md">
<Circle className="size-5" />
Button md
</Button>
<Button variant="outline" size="lg">
<Circle className="size-5" />
Button lg
</Button>
<Button variant="outline" size="xl">
<Circle className="size-5" />
Button xl
</Button>
</div>
</div>
{/* Ghost with Icon Leading */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Ghost</h3>
<div className="flex gap-4">
<Button variant="ghost" size="sm">
<Circle className="size-5" />
Button sm
</Button>
<Button variant="ghost" size="md">
<Circle className="size-5" />
Button md
</Button>
<Button variant="ghost" size="lg">
<Circle className="size-5" />
Button lg
</Button>
<Button variant="ghost" size="xl">
<Circle className="size-5" />
Button xl
</Button>
</div>
</div>
</div>
);
}
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-solid";
export default function ButtonIconLeading() {
return (
<div class="flex flex-col gap-8">
{/* Primary with Icon Leading */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Primary</h3>
<div class="flex gap-4">
<Button size="sm">
<Circle class="size-5" />
Button sm
</Button>
<Button size="md">
<Circle class="size-5" />
Button md
</Button>
<Button size="lg">
<Circle class="size-5" />
Button lg
</Button>
<Button size="xl">
<Circle class="size-5" />
Button xl
</Button>
</div>
</div>
{/* Outline with Icon Leading */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Outline</h3>
<div class="flex gap-4">
<Button variant="outline" size="sm">
<Circle class="size-5" />
Button sm
</Button>
<Button variant="outline" size="md">
<Circle class="size-5" />
Button md
</Button>
<Button variant="outline" size="lg">
<Circle class="size-5" />
Button lg
</Button>
<Button variant="outline" size="xl">
<Circle class="size-5" />
Button xl
</Button>
</div>
</div>
{/* Ghost with Icon Leading */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Ghost</h3>
<div class="flex gap-4">
<Button variant="ghost" size="sm">
<Circle class="size-5" />
Button sm
</Button>
<Button variant="ghost" size="md">
<Circle class="size-5" />
Button md
</Button>
<Button variant="ghost" size="lg">
<Circle class="size-5" />
Button lg
</Button>
<Button variant="ghost" size="xl">
<Circle class="size-5" />
Button xl
</Button>
</div>
</div>
</div>
);
}
Icon Trailing
Buttons with trailing icons display icons after the text. This pattern is useful for indicating direction, navigation, or additional context.
Primary
Outline
Ghost
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-react";
export default function ButtonIconTrailing() {
return (
<div className="flex flex-col gap-8">
{/* Primary with Icon Trailing */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Primary</h3>
<div className="flex gap-4">
<Button size="sm">
Button sm
<Circle className="size-5" />
</Button>
<Button size="md">
Button md
<Circle className="size-5" />
</Button>
<Button size="lg">
Button lg
<Circle className="size-5" />
</Button>
<Button size="xl">
Button xl
<Circle className="size-5" />
</Button>
</div>
</div>
{/* Outline with Icon Trailing */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Outline</h3>
<div className="flex gap-4">
<Button variant="outline" size="sm">
Button sm
<Circle className="size-5" />
</Button>
<Button variant="outline" size="md">
Button md
<Circle className="size-5" />
</Button>
<Button variant="outline" size="lg">
Button lg
<Circle className="size-5" />
</Button>
<Button variant="outline" size="xl">
Button xl
<Circle className="size-5" />
</Button>
</div>
</div>
{/* Ghost with Icon Trailing */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Ghost</h3>
<div className="flex gap-4">
<Button variant="ghost" size="sm">
Button sm
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="md">
Button md
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="lg">
Button lg
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="xl">
Button xl
<Circle className="size-5" />
</Button>
</div>
</div>
</div>
);
}
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-solid";
export default function ButtonIconTrailing() {
return (
<div class="flex flex-col gap-8">
{/* Primary with Icon Trailing */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Primary</h3>
<div class="flex gap-4">
<Button size="sm">
Button sm
<Circle class="size-5" />
</Button>
<Button size="md">
Button md
<Circle class="size-5" />
</Button>
<Button size="lg">
Button lg
<Circle class="size-5" />
</Button>
<Button size="xl">
Button xl
<Circle class="size-5" />
</Button>
</div>
</div>
{/* Outline with Icon Trailing */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Outline</h3>
<div class="flex gap-4">
<Button variant="outline" size="sm">
Button sm
<Circle class="size-5" />
</Button>
<Button variant="outline" size="md">
Button md
<Circle class="size-5" />
</Button>
<Button variant="outline" size="lg">
Button lg
<Circle class="size-5" />
</Button>
<Button variant="outline" size="xl">
Button xl
<Circle class="size-5" />
</Button>
</div>
</div>
{/* Ghost with Icon Trailing */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Ghost</h3>
<div class="flex gap-4">
<Button variant="ghost" size="sm">
Button sm
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="md">
Button md
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="lg">
Button lg
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="xl">
Button xl
<Circle class="size-5" />
</Button>
</div>
</div>
</div>
);
}
Icon Only
Icon-only buttons contain only an icon without text. They're useful for compact interfaces, toolbars, or when space is limited. Always include an aria-label for accessibility.
Primary
Outline
Ghost
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-react";
export default function ButtonIconOnly() {
return (
<div className="flex flex-col gap-8">
{/* Primary Icon Only */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Primary</h3>
<div className="flex gap-4">
<Button size="sm" aria-label="Button sm">
<Circle className="size-5" />
</Button>
<Button size="md" aria-label="Button md">
<Circle className="size-5" />
</Button>
<Button size="lg" aria-label="Button lg">
<Circle className="size-5" />
</Button>
<Button size="xl" aria-label="Button xl">
<Circle className="size-5" />
</Button>
</div>
</div>
{/* Outline Icon Only */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Outline</h3>
<div className="flex gap-4">
<Button variant="outline" size="sm" aria-label="Button sm">
<Circle className="size-5" />
</Button>
<Button variant="outline" size="md" aria-label="Button md">
<Circle className="size-5" />
</Button>
<Button variant="outline" size="lg" aria-label="Button lg">
<Circle className="size-5" />
</Button>
<Button variant="outline" size="xl" aria-label="Button xl">
<Circle className="size-5" />
</Button>
</div>
</div>
{/* Ghost Icon Only */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Ghost</h3>
<div className="flex gap-4">
<Button variant="ghost" size="sm" aria-label="Button sm">
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="md" aria-label="Button md">
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="lg" aria-label="Button lg">
<Circle className="size-5" />
</Button>
<Button variant="ghost" size="xl" aria-label="Button xl">
<Circle className="size-5" />
</Button>
</div>
</div>
</div>
);
}
import { Button } from "@/components/ui/button";
import { Circle } from "lucide-solid";
export default function ButtonIconOnly() {
return (
<div class="flex flex-col gap-8">
{/* Primary Icon Only */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Primary</h3>
<div class="flex gap-4">
<Button size="sm" aria-label="Button sm">
<Circle class="size-5" />
</Button>
<Button size="md" aria-label="Button md">
<Circle class="size-5" />
</Button>
<Button size="lg" aria-label="Button lg">
<Circle class="size-5" />
</Button>
<Button size="xl" aria-label="Button xl">
<Circle class="size-5" />
</Button>
</div>
</div>
{/* Outline Icon Only */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Outline</h3>
<div class="flex gap-4">
<Button variant="outline" size="sm" aria-label="Button sm">
<Circle class="size-5" />
</Button>
<Button variant="outline" size="md" aria-label="Button md">
<Circle class="size-5" />
</Button>
<Button variant="outline" size="lg" aria-label="Button lg">
<Circle class="size-5" />
</Button>
<Button variant="outline" size="xl" aria-label="Button xl">
<Circle class="size-5" />
</Button>
</div>
</div>
{/* Ghost Icon Only */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Ghost</h3>
<div class="flex gap-4">
<Button variant="ghost" size="sm" aria-label="Button sm">
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="md" aria-label="Button md">
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="lg" aria-label="Button lg">
<Circle class="size-5" />
</Button>
<Button variant="ghost" size="xl" aria-label="Button xl">
<Circle class="size-5" />
</Button>
</div>
</div>
</div>
);
}
Disabled
Disabled buttons are non-interactive and visually indicate that an action is currently unavailable. They maintain their visual style but appear faded and cannot be clicked.
Secondary
Outline
Ghost
import { Button } from "@/components/ui/button";
export default function ButtonDisabled() {
return (
<div className="flex flex-col gap-8">
{/* Secondary Disabled */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Secondary</h3>
<div className="flex gap-4">
<Button variant="secondary" size="sm" disabled>
Button sm
</Button>
<Button variant="secondary" size="md" disabled>
Button md
</Button>
<Button variant="secondary" size="lg" disabled>
Button lg
</Button>
<Button variant="secondary" size="xl" disabled>
Button xl
</Button>
</div>
</div>
{/* Outline Disabled */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Outline</h3>
<div className="flex gap-4">
<Button variant="outline" size="sm" disabled>
Button sm
</Button>
<Button variant="outline" size="md" disabled>
Button md
</Button>
<Button variant="outline" size="lg" disabled>
Button lg
</Button>
<Button variant="outline" size="xl" disabled>
Button xl
</Button>
</div>
</div>
{/* Ghost Disabled */}
<div className="flex flex-col gap-4">
<h3 className="text-sm font-semibold text-foreground">Ghost</h3>
<div className="flex gap-4">
<Button variant="ghost" size="sm" disabled>
Button sm
</Button>
<Button variant="ghost" size="md" disabled>
Button md
</Button>
<Button variant="ghost" size="lg" disabled>
Button lg
</Button>
<Button variant="ghost" size="xl" disabled>
Button xl
</Button>
</div>
</div>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonDisabled() {
return (
<div class="flex flex-col gap-8">
{/* Secondary Disabled */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Secondary</h3>
<div class="flex gap-4">
<Button variant="secondary" size="sm" disabled>
Button sm
</Button>
<Button variant="secondary" size="md" disabled>
Button md
</Button>
<Button variant="secondary" size="lg" disabled>
Button lg
</Button>
<Button variant="secondary" size="xl" disabled>
Button xl
</Button>
</div>
</div>
{/* Outline Disabled */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Outline</h3>
<div class="flex gap-4">
<Button variant="outline" size="sm" disabled>
Button sm
</Button>
<Button variant="outline" size="md" disabled>
Button md
</Button>
<Button variant="outline" size="lg" disabled>
Button lg
</Button>
<Button variant="outline" size="xl" disabled>
Button xl
</Button>
</div>
</div>
{/* Ghost Disabled */}
<div class="flex flex-col gap-4">
<h3 class="text-sm font-semibold text-foreground">Ghost</h3>
<div class="flex gap-4">
<Button variant="ghost" size="sm" disabled>
Button sm
</Button>
<Button variant="ghost" size="md" disabled>
Button md
</Button>
<Button variant="ghost" size="lg" disabled>
Button lg
</Button>
<Button variant="ghost" size="xl" disabled>
Button xl
</Button>
</div>
</div>
</div>
);
}
Shapes
Buttons can have different border radius styles. Use rounded-none for rectangular buttons, rounded-full for pill-shaped buttons, or keep the default rounded-lg for slightly rounded corners.
import { Button } from "@/components/ui/button";
export default function ButtonShapes() {
return (
<div className="flex flex-wrap gap-4">
<Button className="rounded-none">Rectangular</Button>
<Button>Square</Button>
<Button className="rounded-full">Rounded</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
export default function ButtonShapes() {
return (
<div class="flex flex-wrap gap-4">
<Button class="rounded-none">Rectangular</Button>
<Button>Square</Button>
<Button class="rounded-full">Rounded</Button>
</div>
);
}
Loading
Buttons support loading states with a spinner. You can show the loading spinner alone or keep the text visible while loading.
import { Button } from "@/components/ui/button";
import { Loader } from "lucide-react";
export default function ButtonLoading() {
return (
<div className="flex flex-wrap gap-4">
<Button size="sm">
<Loader className="animate-spin" />
</Button>
<Button>
<Loader className="animate-spin" />
Please wait
</Button>
</div>
);
}
import { Button } from "@/components/ui/button";
import { Loader } from "lucide-solid";
export default function ButtonLoading() {
return (
<div class="flex flex-wrap gap-4">
<Button size="sm">
<Loader class="animate-spin" />
</Button>
<Button>
<Loader class="animate-spin" />
Please wait
</Button>
</div>
);
}