FluxUI Pro is live - modern UI, powerful animations, zero hassle.
Components
Modal

Modal

Ultra-modern modal component with backdrop blur, smooth animations, and customizable positioning for dialogs and overlays.

Modal Title

This is the modal content. You can put any content here.

Installation

1

Install the packages

npm i motion clsx tailwind-merge lucide-react
2

Add util file

lib/util.ts
import { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
3

Copy and paste the following code into your project

modal.tsx
"use client"; import { motion, AnimatePresence } from "motion/react"; import React, { useEffect, useCallback } from "react"; import { cn } from "@/lib/utils"; import { X } from "lucide-react"; interface ModalProps { isOpen: boolean; onClose?: () => void; title?: string; description?: string; children?: React.ReactNode; size?: "sm" | "md" | "lg" | "xl" | "full"; variant?: "default" | "glass" | "blur" | "minimal"; position?: "center" | "top" | "bottom"; showCloseButton?: boolean; closeOnBackdropClick?: boolean; closeOnEscape?: boolean; preventScroll?: boolean; footer?: React.ReactNode; className?: string; } const Modal: React.FC<ModalProps> = ({ isOpen, onClose, title, description, children, size = "md", variant = "default", position = "center", showCloseButton = true, closeOnBackdropClick = true, closeOnEscape = true, preventScroll = true, footer, className, }) => { // Handle escape key const handleEscape = useCallback((event: KeyboardEvent) => { if (event.key === "Escape" && closeOnEscape && onClose) { onClose(); } }, [closeOnEscape, onClose]); // Handle body scroll prevention useEffect(() => { if (isOpen && preventScroll) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = "unset"; } return () => { document.body.style.overflow = "unset"; }; }, [isOpen, preventScroll]); // Handle escape key listener useEffect(() => { if (isOpen) { document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); } }, [isOpen, handleEscape]); // Size classes const sizeClasses = { sm: "max-w-sm", md: "max-w-md", lg: "max-w-lg", xl: "max-w-xl", full: "max-w-4xl", }; // Variant classes const variantClasses = { default: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700", glass: "bg-white/90 dark:bg-gray-800/90 backdrop-blur-xl border border-white/20 dark:border-gray-700/50", blur: "bg-white/80 dark:bg-gray-900/80 backdrop-blur-2xl border border-white/30 dark:border-gray-600/50", minimal: "bg-white dark:bg-gray-800 border-0 shadow-2xl", }; // Position classes const positionClasses = { center: "items-center", top: "items-start pt-20", bottom: "items-end pb-20", }; return ( <AnimatePresence> {isOpen && ( <> {/* Backdrop */} <motion.div className="fixed inset-0 z-40 flex justify-center p-4" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} > {/* Backdrop Overlay */} <motion.div className={cn( "absolute inset-0", variant === "blur" ? "bg-black/20 backdrop-blur-sm" : variant === "glass" ? "bg-black/10 backdrop-blur-md" : "bg-black/50" )} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={closeOnBackdropClick ? onClose : undefined} transition={{ duration: 0.2 }} /> {/* Modal Content */} <motion.div className={cn( "relative z-50 w-full rounded-xl shadow-2xl overflow-hidden", sizeClasses[size], variantClasses[variant], positionClasses[position], className )} initial={{ opacity: 0, scale: 0.9, y: position === "top" ? -20 : position === "bottom" ? 20 : 0 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.9, y: position === "top" ? -20 : position === "bottom" ? 20 : 0 }} transition={{ duration: 0.3, ease: "easeOut" }} > {/* Header */} {(title || showCloseButton) && ( <motion.div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700" initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.2, delay: 0.1 }} > <div className="flex-1"> {title && ( <motion.h2 className="text-lg font-semibold text-gray-900 dark:text-white" initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.2, delay: 0.2 }} > {title} </motion.h2> )} {description && ( <motion.p className="mt-1 text-sm text-gray-600 dark:text-gray-400" initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.2, delay: 0.3 }} > {description} </motion.p> )} </div> {showCloseButton && ( <motion.button onClick={onClose} className="flex-shrink-0 ml-4 p-1 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-200" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.2, delay: 0.4 }} > <X className="w-5 h-5" /> </motion.button> )} </motion.div> )} {/* Body */} <motion.div className="p-6" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.2, delay: 0.2 }} > {children} </motion.div> {/* Footer */} {footer && ( <motion.div className="px-6 py-4 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-700" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.2, delay: 0.3 }} > {footer} </motion.div> )} </motion.div> </motion.div> </> )} </AnimatePresence> ); }; export default Modal;
4

Update the import paths to match your project setup

Props

PropTypeDefaultDescription
isOpenbooleanfalseControls the visibility of the modal.
onClose() => voidundefinedCallback function when modal is closed.
titlestringundefinedModal title text.
descriptionstringundefinedModal description text.
childrenReactNodeundefinedModal content.
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'Modal size variant.
variant'default' | 'glass' | 'blur' | 'minimal''default'Modal visual variant.
position'center' | 'top' | 'bottom''center'Modal positioning.
showCloseButtonbooleantrueShow close button in header.
closeOnBackdropClickbooleantrueClose modal when clicking backdrop.
closeOnEscapebooleantrueClose modal on Escape key press.
preventScrollbooleantruePrevent body scroll when modal is open.
footerReactNodeundefinedModal footer content.
classNamestringundefinedAdditional CSS classes.