The Button component is used to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
export const Example = () => {
return <Button>Button</Button>;
};
If you haven't included Piksel UI in your project, include it first. You can check the installation page.
Copy the following source code and add it to your project.
import { Slot, Slottable } from "@radix-ui/react-slot";
import React, { ButtonHTMLAttributes, forwardRef, useCallback } from "react";
import { tv, VariantProps } from "tailwind-variants";
import { Loader } from "./loader";
export const buttonDisplayName = "Button";
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
VariantProps<typeof buttonStyles> & {
/**
* If true, the button will be rendered as a child component
*/
asChild?: boolean;
/**
* Left content of the button
*/
left?: React.ReactNode;
/**
* Right content of the button
*/
right?: React.ReactNode;
/**
* Loader component to be displayed while loading
*/
loader?: React.ReactNode;
/**
* Position of the loader
*/
loaderPosition?: "left" | "right";
/**
* Content to be displayed while loading.
*/
childrenWhenLoading?: React.ReactNode;
};
/**
* @name Button
* @description The Button component is used to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
*/
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const {
asChild,
left,
right,
loader = <Loader />,
loaderPosition = "right",
childrenWhenLoading,
variant,
color,
size,
radius,
square,
truncate,
fullWidth,
loading,
className,
children,
...rest
} = props;
const componentClassNames = buttonStyles({
variant,
color,
size,
radius,
square,
truncate,
fullWidth,
loading,
className,
});
const LeftRenderer = useCallback(() => {
if (loading && loaderPosition === "left") {
return <span>{loader}</span>;
}
return left;
}, [loading, loaderPosition, loader, left]);
const RightRenderer = useCallback(() => {
if (loading && loaderPosition === "right") {
return <span>{loader}</span>;
}
return right;
}, [loading, loaderPosition, loader, right]);
const Component = asChild ? Slot : "button";
return (
<Component className={componentClassNames} {...rest} ref={ref}>
<LeftRenderer />
<Slottable>
{loading && childrenWhenLoading ? childrenWhenLoading : children}
</Slottable>
<RightRenderer />
</Component>
);
}
);
Button.displayName = buttonDisplayName;
export const buttonStyles = tv({
/**
* Base styles of the button
*/
base: [
"relative inline-flex items-center justify-center no-underline outline-0",
"font-medium ring-0 ring-offset-0 transition-all cursor-pointer select-none flex-shrink-0 max-w-full whitespace-nowrap overflow-hidden",
"hover:bg-opacity-90",
"focus-visible:ring-2 focus-visible:ring-offset-2",
"active:bg-opacity-100",
"disabled:opacity-75 disabled:cursor-not-allowed",
],
variants: {
/**
* Variant prop of the button
*/
variant: {
filled: "border",
soft: "hover:bg-opacity-50",
outline: "border focus-visible:ring-transparent",
ghost: "active:bg-opacity-50",
link: "underline-offset-4 hover:underline",
},
/**
* Color prop of the button
*/
color: {
blue: "ring-blue-300 border-blue-600",
red: "ring-red-300 border-red-600",
indigo: "ring-indigo-300 border-indigo-600",
green: "ring-green-300 border-green-600",
yellow: "ring-yellow-200 border-yellow-500",
zinc: "ring-zinc-300 border-zinc-600",
white: "ring-zinc-50 border-zinc-50",
black: "ring-zinc-400 border-zinc-950",
},
/**
* Size prop of the button
*/
size: {
xs: "px-2 text-xs h-8 gap-1",
sm: "px-3 text-sm h-9 gap-1.5",
md: "px-4 text-md h-10 gap-2",
lg: "px-5 text-lg h-11 gap-2.5",
xl: "px-5 text-xl h-12 gap-3",
},
radius: {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
xl: "rounded-xl",
full: "rounded-full",
},
square: {
true: "px-0",
},
truncate: {
true: "truncate line-clamp-1",
},
fullWidth: {
true: "flex w-full",
},
loading: {
true: "",
},
},
compoundVariants: [
/* -------------------------------------------------------------------------- */
/* Variants and colors */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* Filled variant */
/* -------------------------------------------------------------------------- */
{
variant: "filled",
color: "blue",
className: "bg-blue-500 text-white hover:text-white",
},
{
variant: "filled",
color: "red",
className: "bg-red-500 text-white hover:text-white",
},
{
variant: "filled",
color: "indigo",
className: "bg-indigo-500 text-white hover:text-white",
},
{
variant: "filled",
color: "green",
className: "bg-green-500 text-white hover:text-white",
},
{
variant: "filled",
color: "yellow",
className: "bg-yellow-400 text-black hover:text-black",
},
{
variant: "filled",
color: "zinc",
className: "bg-zinc-500 text-white hover:text-white",
},
{
variant: "filled",
color: "white",
className: "bg-zinc-50 text-zinc-950 hover:text-zinc-950",
},
{
variant: "filled",
color: "black",
className: "bg-zinc-950 text-white hover:text-white",
},
/* -------------------------------------------------------------------------- */
/* Soft variant */
/* -------------------------------------------------------------------------- */
{
variant: "soft",
color: "blue",
className: "bg-blue-100 text-blue-600 hover:text-blue-600",
},
{
variant: "soft",
color: "red",
className: "bg-red-100 text-red-600 hover:text-red-600",
},
{
variant: "soft",
color: "indigo",
className: "bg-indigo-100 text-indigo-600 hover:text-indigo-600",
},
{
variant: "soft",
color: "green",
className: "bg-green-100 text-green-600 hover:text-green-600",
},
{
variant: "soft",
color: "yellow",
className: "bg-yellow-100 text-yellow-600 hover:text-yellow-600",
},
{
variant: "soft",
color: "zinc",
className: "bg-zinc-100 text-zinc-600 hover:text-zinc-600",
},
{
variant: "soft",
color: "white",
className: "bg-white text-zinc-950 hover:bg-zinc-100 hover:text-zinc-950",
},
{
variant: "soft",
color: "black",
className: "bg-zinc-200 text-zinc-600",
},
/* -------------------------------------------------------------------------- */
/* Outline variant */
/* -------------------------------------------------------------------------- */
{
variant: "outline",
color: "blue",
className: [
"text-blue-500 outline-blue-500",
"hover:bg-blue-500 hover:text-white",
"focus-visible:ring-offset-blue-300",
],
},
{
variant: "outline",
color: "red",
className: [
"text-red-500 outline-red-500",
"hover:bg-red-500 hover:text-white",
"focus-visible:ring-offset-red-300",
],
},
{
variant: "outline",
color: "indigo",
className: [
"text-indigo-500 outline-indigo-500",
"hover:bg-indigo-500 hover:text-white",
"focus-visible:ring-offset-indigo-300",
],
},
{
variant: "outline",
color: "green",
className: [
"text-green-500 outline-green-500",
"hover:bg-green-500 hover:text-white",
"focus-visible:ring-offset-green-300",
],
},
{
variant: "outline",
color: "yellow",
className: [
"text-yellow-600",
"hover:bg-yellow-500 hover:text-black",
"focus-visible:ring-offset-yellow-300",
],
},
{
variant: "outline",
color: "zinc",
className: [
"text-zinc-500",
"hover:bg-zinc-500 hover:text-white",
"focus-visible:ring-offset-zinc-300",
],
},
{
variant: "outline",
color: "white",
className: [
"text-zinc-500",
"hover:bg-zinc-500 hover:text-white",
"focus-visible:ring-offset-zinc-300",
],
},
{
variant: "outline",
color: "black",
className: [
"text-zinc-950",
"hover:bg-zinc-950 hover:text-white",
"focus-visible:ring-offset-zinc-400",
],
},
/* -------------------------------------------------------------------------- */
/* Ghost variant */
/* -------------------------------------------------------------------------- */
{
variant: "ghost",
color: "blue",
className: "text-blue-500 hover:bg-blue-100 hover:text-blue-500",
},
{
variant: "ghost",
color: "red",
className: "text-red-500 hover:bg-red-100 hover:text-red-500",
},
{
variant: "ghost",
color: "indigo",
className: "text-indigo-500 hover:bg-indigo-100 hover:text-indigo-500",
},
{
variant: "ghost",
color: "green",
className: "text-green-500 hover:bg-green-100 hover:text-green-500",
},
{
variant: "ghost",
color: "yellow",
className: "text-yellow-500 hover:bg-yellow-100 hover:text-yellow-500",
},
{
variant: "ghost",
color: "zinc",
className: "text-zinc-500 hover:bg-zinc-100 hover:text-zinc-500",
},
{
variant: "ghost",
color: "white",
className: "text-zinc-950 hover:text-zinc-950",
},
{
variant: "ghost",
color: "black",
className: "text-zinc-600 hover:bg-zinc-950 hover:text-zinc-50",
},
/* -------------------------------------------------------------------------- */
/* Link variant */
/* -------------------------------------------------------------------------- */
{
variant: "link",
color: "blue",
className: "text-blue-500 hover:text-blue-500",
},
{
variant: "link",
color: "red",
className: "text-red-500 hover:text-red-500",
},
{
variant: "link",
color: "indigo",
className: "text-indigo-500 hover:text-indigo-500",
},
{
variant: "link",
color: "green",
className: "text-green-500 hover:text-green-500",
},
{
variant: "link",
color: "yellow",
className: "text-yellow-500 hover:text-yellow-500",
},
{
variant: "link",
color: "zinc",
className: "text-zinc-500 hover:text-zinc-500",
},
{
variant: "link",
color: "white",
className: "text-zinc-100 hover:text-zinc-100",
},
{
variant: "link",
color: "black",
className: "text-zinc-950 hover:text-zinc-950",
},
/* -------------------------------------------------------------------------- */
/* Square and sizes */
/* -------------------------------------------------------------------------- */
{
square: true,
size: "xs",
className: "size-8",
},
{
square: true,
size: "sm",
className: "size-9",
},
{
square: true,
size: "md",
className: "size-10",
},
{
square: true,
size: "lg",
className: "size-11",
},
{
square: true,
size: "xl",
className: "size-12",
},
],
defaultVariants: {
variant: "filled",
color: "blue",
size: "md",
radius: "md",
},
});
The Button component has the following props.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild? | boolean | false | The component used for the root node. |
variant? | 'filled' | 'soft' | 'outline' | 'ghost' | 'link' | 'filled' | The variant of the button. |
color? | 'blue' | 'red' | 'indigo' | 'green' | 'yellow' | 'zinc' | 'white' | 'black' | 'blue' | The color of the button. |
size? | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The size of the button. |
radius? | 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'md' | The radius of the button. |
square? | boolean | false | If true, the button will be square. |
truncate? | boolean | false | If true, the button text will be truncated. |
fullWidth? | boolean | false | If true, the button will be full width. |
loading? | boolean | false | If true, the button will be in a loading state. |
left? | React.ReactNode | Left content of the button. | |
right? | React.ReactNode | Right content of the button. | |
loader? | React.ReactNode | <Loader /> | Loader component to be displayed while loading. |
loaderPosition? | 'left' | 'right' | 'right' | Position of the loader. |
childrenWhenLoading? | React.ReactNode | Content to be displayed while loading. |