logo

Nurav UI

Community
Components

Button

Highly interactive, multipurpose button component with support for multiple variants and states.Copy Markdown

Preview

Explore six distinct visual variants designed to fit any section of your application.

Variant V1 (Modern)

Fluid, rounded design with soft shadows. Ideal for primary CTAs.

Loading...

Variant V2 (Glassmorphism)

Subtle backdrop-blur with a semi-transparent background. Perfect for dark or vibrant backgrounds.

Variant V3 (Industrial/Minimal)

Sharp corners, bold borders, and an offset shadow for a high-impact, brutalist look.

Loading...

Variant V4 (Ghost)

Minimalist button without background or borders, showing only on hover.

Variant V5 (Outline)

Classic outlined style that fills on interaction. Great for secondary actions.

Loading...

Variant V6 (Minimalist)

A very subtle, clean design with a light background and soft border. Perfect for low-priority actions or secondary information.

Loading...

Features

Audible Feedback

By default, these buttons provide subtle audible feedback on interaction. You can control this behavior globally using the Sound Toggle component or disable it per-instance via the disableSound prop.

Icons, States & Sound

Easily add icons, trigger a loading state, or toggle audible feedback.

<Button leftIcon={<Rocket size={16} />}>Launch App</Button>
<Button isLoading>Processing</Button>

Sizes

Available in small, medium, and large.

Installation

Automatic (CLI)

The fastest way to get started is using the shadcn CLI.

Before using the @nurav-ui alias, add this to your components.json:

components.json
{
  "registries": {
    "@nurav-ui": "https://nurav-ui.vercel.app/r"
  }
}

Or skip alias setup entirely and use the direct URL below.

Terminal
npx shadcn@latest add @nurav-ui/button

Alternatively, install via direct URL (no components.json changes needed):

Terminal
npx shadcn@latest add https://nurav-ui.vercel.app/r/button.json

Manual Setup

If you prefer manual configuration, follow these steps:

Install Dependencies

Install the necessary animation and icon libraries:

Terminal
npm install motion lucide-react clsx tailwind-merge

Create/Customize Component

Copy the source code into your project (e.g., components/nurav-ui/Button.tsx). You can modify this code to create your own custom button designs:

Button.tsx
"use client";

import React from "react";
import { motion, HTMLMotionProps } from "motion/react";
import { cn } from "@/lib/utils";
import { Loader2 } from "lucide-react";

import { useSound } from "@/hooks/use-sound";

interface ButtonProps extends Omit<HTMLMotionProps<"button">, "children"> {
  children?: React.ReactNode;
  variant?: "v1" | "v2" | "v3" | "v4" | "v5" | "v6";
  size?: "sm" | "md" | "lg" | "icon";
  isLoading?: boolean;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  disableSound?: boolean;
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      className,
      variant = "v1",
      size = "md",
      isLoading = false,
      leftIcon,
      rightIcon,
      children,
      disabled,
      disableSound = false,
      onClick,
      ...props
    },
    ref,
  ) => {
    const { mouseClick } = useSound();

    const sizeStyles = {
      sm: "h-9 px-3 text-xs gap-1.5",
      md: "h-11 px-5 text-sm gap-2",
      lg: "h-13 px-8 text-base gap-2.5",
      icon: "h-10 w-10 flex items-center justify-center p-0",
    };

    const getVariantStyles = () => {
      switch (variant) {
        case "v1":
          return "rounded-full bg-foreground text-background shadow-[0_4px_14px_0_rgba(0,0,0,0.1)] hover:shadow-[0_6px_20px_rgba(0,0,0,0.15)] hover:bg-foreground/90 active:scale-95 transition-all duration-200";
        case "v2":
          return "rounded-xl bg-foreground/5 backdrop-blur-md border text-foreground  active:scale-95 transition-all duration-200";
        case "v3":
          return "rounded-none border-2 border-foreground bg-background text-foreground font-bold uppercase shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] dark:shadow-[4px_4px_0px_0px_rgba(255,255,255,0.3)] hover:translate-x-[1px] hover:translate-y-[1px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] active:translate-x-[2px] active:translate-y-[2px] active:shadow-none transition-all duration-100";
        case "v4":
          return "rounded-lg bg-transparent text-foreground hover:bg-foreground/10 active:scale-95 transition-all duration-200";
        case "v5":
          return "rounded-xl border-2 border-foreground/20 bg-transparent text-foreground hover:border-foreground hover:bg-foreground/5 active:scale-95 transition-all duration-200";
        case "v6":
          return "rounded-md border border-foreground/10 text-foreground bg-foreground/2";
        default:
          return "";
      }
    };

    return (
      <motion.button
        ref={ref}
        disabled={isLoading || disabled}
        className={cn(
          "inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
          sizeStyles[size],
          getVariantStyles(),
          className,
        )}
        onClick={(e) => {
          if (!disableSound) mouseClick();
          if (onClick) onClick(e);
        }}
        {...props}
      >
        {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
        {!isLoading && leftIcon && (
          <span className="flex shrink-0">{leftIcon}</span>
        )}
        <span className="truncate">{children}</span>
        {!isLoading && rightIcon && (
          <span className="flex shrink-0">{rightIcon}</span>
        )}
      </motion.button>
    );
  },
);

Button.displayName = "Button";

Props

PropTypeDefaultDescription
variant
'v1' | 'v2' | 'v3' | 'v4' | 'v5' | 'v6''v1'The visual style of the button.
size
'sm' | 'md' | 'lg' | 'icon''md'The size of the button.
isLoading
booleanfalseShows a loading spinner and disables the button.
leftIcon
ReactNodeundefinedIcon displayed before the text.
rightIcon
ReactNodeundefinedIcon displayed after the text.
disabled
booleanfalseWhether the button is interactive.
disableSound
booleanfalseWhen true, disables the audible click feedback for this instance.
className
stringundefinedAdditional CSS classes.

Built by Varun

Last Updated:April 11, 2026

On this page