Components
Modal A premium modal component with spring animations, backdrop blurring, and integrated sound effects. Copy Markdown
The Modal component provides a focused overlay for important content or interactions.
The standard modal is suitable for complex forms, large content areas, or detailed information.
The alert variant is a compact, centered dialog with deep backdrop blur and rounded corners, specifically designed for quick confirmations or destructive actions.
< Modal variant = "alert" ... />
Backdrop Blur : Uses backdrop-blur for a modern, glassmorphic appearance.
Spring Animations : Framer Motion powered entry and exit transitions.
Audible Feedback : Integrated useSound hook for premium clicking sounds.
Accessible : Handles Escape key to close and click-outside behavior.
Responsive : Adapts gracefully to mobile devices with centered layouts.
Before using the @nurav-ui alias , add this to your components.json:
{
"registries" : {
"@nurav-ui" : "https://nurav-ui.vercel.app/r"
}
}
npx shadcn@latest add @nurav-ui/modal
Alternatively , install via direct URL:
npx shadcn@latest add https://nurav-ui.vercel.app/r/modal.json
Install Dependencies npm install motion lucide-react clsx tailwind-merge Create Component Copy the code below into your project (e.g., components/nurav-ui/Modal.tsx):
"use client" ;
import { useEffect, useState } from "react" ;
import { createPortal } from "react-dom" ;
import { motion, AnimatePresence } from "motion/react" ;
import { X } from "lucide-react" ;
import { cn } from "@/lib/utils" ;
import { useSound } from "@/registry/hooks/use-sound" ;
interface ModalProps {
isOpen : boolean ;
onClose : () => void ;
title ?: string ;
description ?: string ;
children : React . ReactNode ;
className ?: string ;
showCloseButton ?: boolean ;
variant ?: "default" | "alert" ;
containerRef ?: React . RefObject < HTMLElement | null >;
}
export function Modal ({
isOpen ,
onClose ,
title ,
description ,
children ,
className ,
showCloseButton = true ,
variant = "default" ,
containerRef ,
} : ModalProps ) {
const { clickOn , clickOff } = useSound ();
const [ mounted , setMounted ] = useState ( false );
useEffect (() => {
setMounted ( true );
}, []);
useEffect (() => {
if (isOpen) clickOn ();
}, [isOpen, clickOn]);
const handleClose = () => {
clickOff ();
onClose ();
};
useEffect (() => {
const handleEsc = ( e : KeyboardEvent ) => {
if (e.key === "Escape" ) handleClose ();
};
if (isOpen) {
window. addEventListener ( "keydown" , handleEsc);
document.body.style.overflow = "hidden" ;
} else {
document.body.style.overflow = "" ;
}
return () => {
window. removeEventListener ( "keydown" , handleEsc);
document.body.style.overflow = "" ;
};
}, [isOpen]);
if ( ! mounted) return null ;
return createPortal (
< AnimatePresence >
{isOpen && (
< div className = "fixed inset-0 z-9999 flex items-center justify-center p-4" >
< motion.div
initial = {{ opacity: 0 }}
animate = {{ opacity: 1 }}
exit = {{ opacity: 0 }}
onClick = {handleClose}
className = "absolute inset-0 bg-black/40 backdrop-blur-md"
/>
< motion.div
initial = {{ opacity: 0 , scale: 0.9 , y: 20 }}
animate = {{ opacity: 1 , scale: 1 , y: 0 }}
exit = {{ opacity: 0 , scale: 0.9 , y: 20 }}
transition = {{ type: "spring" , damping: 25 , stiffness: 350 }}
className = { cn (
"relative w-full overflow-hidden transition-all duration-300" ,
variant === "alert"
? "max-w-[400px] rounded-[40px] bg-[#1c1c1e]/80 p-10 text-center backdrop-blur-3xl border border-white/10 shadow-[0_24px_50px_-12px_rgba(0,0,0,0.5)]"
: "max-w-lg rounded-2xl bg-white p-6 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 shadow-2xl" ,
className,
)}
>
{showCloseButton && variant !== "alert" && (
< button
onClick = {handleClose}
className = "absolute right-4 top-4 rounded-full p-1 text-neutral-500 transition-colors hover:bg-neutral-100 dark:hover:bg-neutral-800"
>
< X size = { 18 } />
</ button >
)}
{(title || description) && (
< div
className = { cn (
"space-y-3" ,
variant === "alert" ? "mb-10" : "mb-6" ,
)}
>
{title && (
< h2
className = { cn (
"font-semibold tracking-tight" ,
variant === "alert"
? "text-[22px] leading-tight text-white/90 px-2"
: "text-xl text-neutral-900 dark:text-neutral-100" ,
)}
>
{title}
</ h2 >
)}
{description && (
< p
className = { cn (
"text-sm" ,
variant === "alert"
? "text-neutral-400 font-medium"
: "text-neutral-500 dark:text-neutral-400" ,
)}
>
{description}
</ p >
)}
</ div >
)}
< div className = "relative" >{children}</ div >
</ motion.div >
</ div >
)}
</ AnimatePresence >,
containerRef?.current || document.body,
);
}
Last Updated: April 14, 2026