Fiber UI LogoFiberUI

Button

The button component

Basic Usage

import { Button } from "@repo/ui/components/button";
import { Info } from "lucide-react";

/* BASIC USAGE EXAMPLES */
export const Example1 = () => {
    return (
        <div className="flex flex-wrap items-center justify-center gap-4">
            <Button>Default</Button>
            <Button variant={"outline"}>Outline</Button>
            <Button size={"icon"} variant={"outline"}>
                <Info />
            </Button>
        </div>
    );
};

Sizes

import { Button } from "@repo/ui/components/button";
import { BusIcon, Info, MenuSquareIcon } from "lucide-react";

export const Example2 = () => {
    return (
        <div>
            <div className="flex flex-wrap items-center justify-center gap-4">
                <Button size={"sm"} variant={"outline"}>
                    Small
                </Button>
                <Button size={"default"} variant={"outline"}>
                    Default
                </Button>
                <Button size={"lg"} variant={"outline"}>
                    Large
                </Button>
            </div>
            <div className="mt-5 flex flex-wrap items-center justify-center gap-4">
                <Button size={"icon-sm"} variant={"outline"}>
                    <Info />
                </Button>
                <Button size={"icon"} variant={"outline"}>
                    <MenuSquareIcon />
                </Button>
                <Button size={"icon-lg"} variant={"outline"}>
                    <BusIcon />
                </Button>
            </div>
        </div>
    );
};

Variants

import { Button } from "@repo/ui/components/button";

export const Example3 = () => {
    return (
        <div>
            <div className="flex flex-wrap items-center justify-center gap-5 md:gap-4">
                <Button> Default </Button>
                <Button variant={"gradient"}> Gradient </Button>
                <Button variant={"outline"}> Outline </Button>
                <Button variant={"secondary"}> Secondary </Button>
                <Button variant={"ghost"}> Ghost </Button>
                <Button variant={"destructive"}> Destructive </Button>
                <Button variant={"link"}> Link </Button>
            </div>
            <div className="mt-5 flex flex-wrap items-center justify-center gap-5 md:gap-4">
                <Button variant={"adobe"}> Adobe </Button>
                <Button variant={"instagram"}> Instagram </Button>
            </div>
        </div>
    );
};

Component Code

"use client";

import { ReactNode, forwardRef } from "react";

import {
    Button as AriaButton,
    ButtonProps as AriaButtonProps,
    ButtonContext as AriaButtonContext,
} from "react-aria-components";

import { tv, type VariantProps, cn } from "tailwind-variants";

export const buttonVariants = tv({
    base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium outline-none transition-transform duration-200 ease-in-out hover:scale-105 focus-visible:ring-[3px] active:scale-100 disabled:pointer-events-none disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
    variants: {
        variant: {
            default:
                "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 focus-visible:ring-primary/20 dark:focus-visible:ring-primary/40",
            gradient:
                "text-primary-foreground shadow-xs hover:bg-primary/90 bg-linear-to-br from-indigo-600 to-purple-600 hover:from-purple-600 hover:to-indigo-600",
            destructive:
                "bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white",
            outline:
                "bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border",
            secondary:
                "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
            ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
            link: "text-primary underline-offset-4 hover:underline",

            adobe: "rounded-full bg-[#3B63FB] font-semibold text-white shadow-sm hover:bg-[#274dea] focus-visible:ring-1 focus-visible:ring-[#274dea]/40 focus-visible:ring-offset-0 active:bg-[#274dea]",

            instagram:
                "bg-linear-to-tr rounded-full from-[#F58529] via-[#DD2A7B] to-[#515BD4] font-semibold text-white shadow-sm hover:opacity-90 focus-visible:ring-2 focus-visible:ring-[#DD2A7B]/40 focus-visible:ring-offset-2 active:opacity-80",
        },
        size: {
            default: "h-10 px-6 has-[>svg]:px-3",
            sm: "h-9 gap-1.5 px-5 has-[>svg]:px-2.5",
            lg: "h-12 px-7 has-[>svg]:px-4",
            icon: "h-10 w-10",
            "icon-sm": "h-9 w-9",
            "icon-lg": "h-11 w-11",
        },
    },
    defaultVariants: {
        variant: "default",
        size: "default",
    },
});

interface ButtonProps
    extends Omit<AriaButtonProps, "children">,
        VariantProps<typeof buttonVariants> {
    asChild?: boolean;
    children: ReactNode;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
    (props, ref) => {
        const { variant, size, className, children, asChild, ...restProps } =
            props;

        const finalProps = {
            ...restProps,
            ref,
            className: cn(buttonVariants({ variant, size }), className),
            children,
        };

        if (asChild) {
            return (
                <AriaButtonContext value={finalProps}>
                    {children}
                </AriaButtonContext>
            );
        }

        return <AriaButton {...finalProps}>{children}</AriaButton>;
    },
);

Button.displayName = "Button";