Button
Buttons allow the user to take actions or make choices.
Installation
Install the component Button in your project using the CLI.
Button.tsx
pnpm dlx behsseui@latest add ButtonInstall the component manually.
Create a ui folder at the root of the project, then a component folder inside it, and finally a Button.tsx file in that folder.
Copy and paste the following code into your project.
ui/components/Button.tsx
1import type { ButtonHTMLAttributes, ReactNode } from "react";2import { Slot, type AsChildProps } from "./internals/Slot";3import { cva, type VariantProps } from "class-variance-authority"4import { cn } from "@/lib/utils";56const buttonVariants = cva(7 "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none shrink-0 cursor-pointer",8 {9 variants: {10 variant: {11 default: "bg-primary text-primary-foreground hover:bg-primary/85",12 secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/40",13 outline: "border bg-background hover:bg-accent hover:text-accent-foreground",14 destructive: "bg-destructive text-primary-foreground hover:bg-destructive/85",15 ghost: "hover:bg-accent hover:text-accent-foreground",16 text: "text-primary/85 hover:text-primary"17 },18 size: {19 default: "h-9 px-4 py-2 has-[>svg]:px-3",20 small: "h-8 px-3 py-1.5 has-[>svg]:px-2",21 large: "h-10 px-5 py-2.5 has-[>svg]:px-4",22 xl: "h-12 px-6 py-3 has-[>svg]:px-5"23 },24 iconSize: {25 default: "h-9 aspect-square",26 small: "h-8 aspect-square",27 large: "h-10 aspect-square",28 xl: "h-12 aspect-square"29 },30 },31 defaultVariants: {32 variant: "default"33 }34 }35);3637type Props = AsChildProps<38 {39 children: ReactNode;40 className?: string;41 } & VariantProps<typeof buttonVariants>,42 ButtonHTMLAttributes<HTMLButtonElement>43>;4445export function Button({46 variant = "default",47 size,48 iconSize,49 children,50 className,51 asChild,52 ...props53}: Props) {54 const Component = asChild ? Slot : "button";55 return (56 <Component className={cn(buttonVariants({variant, size: iconSize ? undefined : (size ?? "default"), iconSize: iconSize ?? undefined, className}))} {...props}>57 {children}58 </Component>59 );60}For Button.tsx to work, create an internals folder inside ui/component, then create a Slot.tsx file in it with the following code.
ui/components/internals/Slot.tsx
1import {2 Children,3 cloneElement,4 isValidElement,5 type HTMLAttributes,6 type PropsWithChildren,7 type ReactElement,8 type ReactNode,9} from "react";10import { twMerge } from "tailwind-merge";1112// Type utilitaire pour simplifier le typage des composant supportant asChild13export type AsChildProps<BaseProps, SecondaryProps = {}> =14 | ({ asChild: true; children: ReactNode } & BaseProps)15 | ({ asChild?: false; children?: ReactNode } & BaseProps & SecondaryProps);1617export function Slot(props: PropsWithChildren<HTMLAttributes<HTMLElement>>) {18 const children = Children.toArray(props.children).filter((c) =>19 isValidElement(c)20 );2122 if (children.length !== 1) {23 throw new Error("Slot must have exactly one child element");24 }2526 const child = children[0] as ReactElement<HTMLAttributes<HTMLElement>>;2728 return cloneElement(child, {29 ...props,30 ...child.props,31 style:32 props.style || child.props.style33 ? {34 ...props.style,35 ...child.props.style,36 }37 : undefined,38 className:39 props.className || child.props.className40 ? twMerge(props.className, child.props.className)41 : undefined,42 });43}Usages
Different variants and use cases for the Button component.
Default
Default.tsx
<Button>Default</Button>Secondary
Secondary.tsx
<Button variant="secondary">Secondary</Button>Outline
Outline.tsx
<Button variant="outline">Outline</Button>Destructive
Destructive.tsx
<Button variant="destructive">Destructive</Button>Ghost
Ghost.tsx
<Button variant="ghost">Ghost</Button>Text
Text.tsx
<Button variant="text">Text</Button>Small
A smaller button with reduced padding.
Small.tsx
<Button size="small">Small</Button>Large
A larger button with increased padding.
Large.tsx
<Button size="large">Large</Button>XL
An extra large button.
XL.tsx
<Button size="xl">Extra Large</Button>Icon
A square button for icons.
Icon.tsx
<Button iconSize="default">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</Button>Icon Small
A small square button for icons.
Icon Small.tsx
<Button iconSize="small">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</Button>Icon Large
A large square button for icons.
Icon Large.tsx
<Button iconSize="large">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</Button>Icon XL
An extra large square button for icons.
Icon XL.tsx
<Button iconSize="xl">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</Button>