Buttons

Button

A versatile button component with multiple variants and sizes for any action in your application.

API
import { Button } from "@/components/ui/button";
 
export function ButtonDemo() {
  return <Button>Button</Button>;
}

Installation

CLI

npx shadcn@latest add "https://jolyui.dev/r/button"

Manual

Install the following dependencies:

npm install @radix-ui/react-slot class-variance-authority

Copy and paste the following code into your project. component/ui/button.tsx

import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { motion, useMotionTemplate, useMotionValue } from "motion/react";
import * as React from "react";
 
const buttonVariants = cva(
  "relative inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-semibold transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 overflow-hidden group [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default:
          "bg-gradient-to-br from-primary via-primary to-primary/80 text-primary-foreground shadow-lg shadow-primary/25 hover:shadow-xl hover:shadow-primary/30 hover:scale-[1.02] active:scale-[0.98] before:absolute before:inset-0 before:bg-gradient-to-br before:from-white/20 before:to-transparent before:opacity-0 hover:before:opacity-100 before:transition-opacity",
        destructive:
          "bg-gradient-to-br from-destructive via-destructive to-destructive/80 text-destructive-foreground shadow-lg shadow-destructive/25 hover:shadow-xl hover:shadow-destructive/30 hover:scale-[1.02] active:scale-[0.98] before:absolute before:inset-0 before:bg-gradient-to-br before:from-white/20 before:to-transparent before:opacity-0 hover:before:opacity-100 before:transition-opacity",
        outline:
          "border-2 border-input bg-background/50 backdrop-blur-sm shadow-sm hover:bg-accent hover:text-accent-foreground hover:border-accent hover:scale-[1.02] active:scale-[0.98] hover:shadow-md",
        secondary:
          "bg-gradient-to-br from-secondary via-secondary to-secondary/80 text-secondary-foreground shadow-lg shadow-secondary/25 hover:shadow-xl hover:shadow-secondary/30 hover:scale-[1.02] active:scale-[0.98] before:absolute before:inset-0 before:bg-gradient-to-br before:from-white/20 before:to-transparent before:opacity-0 hover:before:opacity-100 before:transition-opacity",
        ghost:
          "hover:bg-accent/80 hover:text-accent-foreground hover:scale-[1.02] active:scale-[0.98] backdrop-blur-sm",
        link: "text-primary underline-offset-4 hover:underline hover:text-primary/80 transition-colors",
        shimmer:
          "relative bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] text-white shadow-2xl shadow-primary/30 hover:shadow-primary/50 animate-shimmer border border-slate-800/50 before:absolute before:inset-0 before:bg-gradient-to-r before:from-transparent before:via-white/10 before:to-transparent before:translate-x-[-200%] before:animate-shimmer-slide hover:scale-[1.02] active:scale-[0.98]",
        glow:
          "relative bg-gradient-to-r from-violet-600 via-purple-600 to-fuchsia-600 text-white shadow-lg shadow-purple-500/50 hover:shadow-2xl hover:shadow-purple-500/60 animate-gradient-x before:absolute before:inset-[-2px] before:rounded-xl before:bg-gradient-to-r before:from-violet-600 before:via-purple-600 before:to-fuchsia-600 before:blur-md before:opacity-75 before:-z-10 hover:scale-[1.02] active:scale-[0.98]",
      },
      size: {
        default: "h-10 px-6 py-2",
        sm: "h-8 rounded-lg px-4 text-xs",
        lg: "h-12 rounded-xl px-10 text-base",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);
 
export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}
 
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, children, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    const mouseX = useMotionValue(0);
    const mouseY = useMotionValue(0);
 
    const handleMouseMove = (e: React.MouseEvent<HTMLButtonElement>) => {
      const { left, top } = e.currentTarget.getBoundingClientRect();
      mouseX.set(e.clientX - left);
      mouseY.set(e.clientY - top);
    };
 
    const background = useMotionTemplate`radial-gradient(circle at ${mouseX}px ${mouseY}px, rgba(255,255,255,0.15), transparent 80%)`;
 
    if (asChild) {
      return (
        <Comp
          className={`${buttonVariants({ variant, size })} ${className || ""}`}
          ref={ref}
          {...props}
        >
          {children}
        </Comp>
      );
    }
 
    const MotionButton = motion.button as any;
 
    return (
      <MotionButton
        className={`${buttonVariants({ variant, size })} ${className || ""}`}
        ref={ref}
        onMouseMove={handleMouseMove}
        whileHover={{ scale: variant === "link" ? 1 : 1.02 }}
        whileTap={{ scale: variant === "link" ? 1 : 0.98 }}
        transition={{ type: "spring", stiffness: 400, damping: 17 }}
        {...props}
      >
        {variant !== "link" && variant !== "ghost" && (
          <motion.div
            className="pointer-events-none absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
            style={{ background }}
          />
        )}
        <span className="relative z-10 flex items-center justify-center gap-2">
          {children}
        </span>
      </MotionButton>
    );
  }
);
Button.displayName = "Button";
 
export { Button, buttonVariants };

Copy and paste the following code into your project. globals.css

@theme inline {
--animate-shimmer: shimmer 2s linear infinite;
--animate-shimmer-slide: shimmer-slide 2s linear infinite;

@keyframes shimmer {
0% {
  background-position: -200% 0;
}
100% {
  background-position: 200% 0;
}
}

@keyframes shimmer-slide {
0% {
  transform: translateX(-200%);
}
100% {
  transform: translateX(200%);
}
}
}

Usage

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

<Button>Click me</Button>

Examples

Variants

The Button component supports multiple visual variants with modern animations and effects.

import { Button } from "@/components/ui/button";
 
export function ButtonVariantsDemo() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Button variant="default">Default</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
      <Button variant="shimmer">Shimmer</Button>
      <Button variant="glow">Glow</Button>
    </div>
  );
}

Available variants:

  • default - Gradient background with hover glow and radial mouse tracking
  • destructive - Red gradient for dangerous actions
  • outline - Bordered with backdrop blur
  • secondary - Secondary gradient style
  • ghost - Transparent with subtle hover
  • link - Text link style
  • shimmer - Animated shimmer effect on hover
  • glow - Purple gradient with glowing border effect

Sizes

Choose from different sizes to match your design needs.

import { Button } from "@/components/ui/button";
 
export function ButtonSizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Button size="sm">Small</Button>
      <Button size="default">Default</Button>
      <Button size="lg">Large</Button>
    </div>
  );
}

With Icons

Combine buttons with icons for enhanced visual communication.

import { ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
 
export function ButtonIconDemo() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Button>
        <ChevronRight />
        With Icon
      </Button>
      <Button size="icon">
        <ChevronRight />
      </Button>
    </div>
  );
}

API Reference

Button

A flexible button component that can be rendered as any element using the asChild prop.

Prop

Type

Notes

  • Use variant="default" for primary actions
  • Use variant="destructive" for dangerous actions like delete
  • Use variant="outline" for secondary actions
  • Use variant="ghost" for tertiary actions
  • Use variant="link" for navigation
  • The asChild prop allows polymorphic rendering with Radix UI's Slot component

How is this guide?