Buttons
Button
A versatile button component with multiple variants and sizes for any action in your application.
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-authorityCopy 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 trackingdestructive- Red gradient for dangerous actionsoutline- Bordered with backdrop blursecondary- Secondary gradient styleghost- Transparent with subtle hoverlink- Text link styleshimmer- Animated shimmer effect on hoverglow- 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
asChildprop allows polymorphic rendering with Radix UI's Slot component
How is this guide?