Button

A framer-motion supported button with size and design variants.

Page Outline

Manual Installation

components/ui/button.tsx
'use client'; import { cn } from '@/helpers/utils'; import { ArrowRight, ChevronRight, Loader2 } from 'lucide-react'; import { ReactNode, forwardRef, useState } from 'react'; import { motion } from 'framer-motion'; import { ComponentAnimationType } from '@/components/configs/animation-config'; import { ComponentAnimation } from '@/components/configs/animation-config'; export type ButtonVariantType = 'primary' | 'secondary' | 'ghost'; export type ButtonSizeType = 'sm' | 'md' | 'lg'; export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { withArrow?: boolean; icon?: ReactNode; iconDirection?: 'left' | 'right'; variant?: ButtonVariantType; size?: ButtonSizeType; stretch?: boolean; isLoading?: boolean; animationType?: ComponentAnimationType; } const ButtonVariantStyles: Record<ButtonVariantType, string> = { primary: 'bg-gradient-to-b from-blue-500 to-blue-600 text-white shadow-md active:shadow', secondary: 'bg-white/10 hover:bg-white/20 focus:ring-neutral-700', ghost: 'bg-transparent hover:bg-white/10', }; const ButtonSizeStyles: Record<ButtonSizeType, string> = { sm: 'text-xs px-4 py-2 rounded-lg', md: 'text-sm px-4 py-2 rounded-lg', lg: 'text-base px-6 py-3 rounded-xl', }; export const Button = forwardRef<HTMLButtonElement, ButtonProps>( ( { className, children, variant = 'primary', size = 'md', icon = <></>, iconDirection = 'left', withArrow = false, stretch = false, disabled = false, isLoading = false, animationType = 'none', ...args }, ref, ) => { const [hovering, setHovering] = useState<boolean>(false); return ( <motion.button initial={{ ...ComponentAnimation[animationType].initial, }} animate={{ ...ComponentAnimation[animationType].animate, }} whileTap={{ scale: isLoading || disabled ? 1 : 0.9, }} ref={ref} className={cn( 'tracking-tight font-medium flex flex-row items-center justify-center gap-1 hover:gap-1.5 outline-none', !disabled && 'hover:brightness-110 active:brightness-90', animationType === 'none' && 'transition-all', ButtonVariantStyles[variant], ButtonSizeStyles[size], stretch && 'w-full', isLoading && 'cursor-default opacity-60 transition-all', disabled && 'cursor-not-allowed opacity-40', className, )} onMouseEnter={(event) => { // toggling from chevron to arrow if (withArrow) setHovering(true); // Run the function passed in args (if it exists) if (args && args.onMouseEnter) { args.onMouseEnter(event); } }} onMouseLeave={(event) => { // toggling back from arrow to chevron setHovering(false); // Run the function passed in args (if it exists) if (args && args.onMouseLeave) { args.onMouseLeave(event); } }} {...(args as unknown as any)}> <span className="button-content-wrapper flex items-center gap-2"> {isLoading && ( <motion.span key={'loader-wrapper'} initial={{ opacity: 0, y: 12, }} animate={{ opacity: 1, y: 0, }}> <Loader2 className="h-4 w-4 animate-spin" /> </motion.span> )} {iconDirection === 'left' && icon} {children} {iconDirection === 'right' && icon} </span> {withArrow && (!hovering ? ( <ChevronRight className="h-4 w-4" /> ) : ( <ArrowRight className="h-4 w-4 animate-pulse" /> ))} </motion.button> ); }, ); Button.displayName = 'Button';

Primary Button

This is the default/primary button design, with subtle blue gradient effect

Secondary Button

This is the secondary button, with subtle background

Ghost Button

This is the ghost button, with trasnparent background and subtle white themed background on interaction

Loading Button

Has support for loading states, by setting isLoading as true

Disabled Button

Button with disabled state. Not accessible if disabled is set as true

Button with arrow

Button component with leading arrow icon

Button with icon

Button component with leading icon. With iconDirection you can manage the icon placement

Small Primary Button

Primary Button with small size

Small Secondary Button

Secondary button with small size

Small Ghost Button

Ghost Button with small size

Large Primary Button

Primary Button with large size

Large Secondary Button

Secondary button with large size

Large Ghost Button

Ghost Button with large size