Basic Modal
smoothui-registry/basic-modalFreeComponent
A BasicModal component for SmoothUI.
Install it
npx shadcn@latest add https://www.smoothui.dev/r/basic-modal.jsonSource
1"use client";23import { X } from "lucide-react";4import { AnimatePresence, motion, useReducedMotion } from "motion/react";5import { useEffect, useRef, useState } from "react";6import { createPortal } from "react-dom";7import { useOnClickOutside } from "usehooks-ts";89export interface BasicModalProps {10 children: React.ReactNode;11 isOpen: boolean;12 onClose: () => void;13 size?: "sm" | "md" | "lg" | "xl" | "full";14 title?: string;15}1617const modalSizes = {18 full: "max-w-4xl",19 lg: "max-w-lg",20 md: "max-w-md",21 sm: "max-w-sm",22 xl: "max-w-xl",23};2425export default function BasicModal({26 isOpen,27 onClose,28 title,29 children,30 size = "md",31}: BasicModalProps) {32 const overlayRef = useRef<HTMLDivElement>(null);33 const modalRef = useRef<HTMLDivElement>(34 null35 ) as React.RefObject<HTMLDivElement>;36 const closeButtonRef = useRef<HTMLButtonElement>(null);37 const previousActiveElementRef = useRef<HTMLElement | null>(null);38 useOnClickOutside(modalRef, () => onClose());39 const [mounted, setMounted] = useState(false);40 const shouldReduceMotion = useReducedMotion();4142 const titleId = title43 ? `modal-title-${Math.random().toString(36).substring(2, 9)}`44 : undefined;4546 useEffect(() => {47 setMounted(true);48 }, []);4950 // Focus management: Save previous focus and restore on close51 useEffect(() => {52 if (isOpen) {53 previousActiveElementRef.current = document.activeElement as HTMLElement;54 // Focus the close button or first focusable element when modal opens55 setTimeout(() => {56 closeButtonRef.current?.focus();57 }, 100);58 } else if (previousActiveElementRef.current) {59 // Restore focus when modal closes60 previousActiveElementRef.current.focus();61 }62 }, [isOpen]);6364 // Close on Escape key press and focus trap65 useEffect(() => {66 if (!isOpen) {67 return;68 }6970 const handleKeyDown = (e: KeyboardEvent) => {71 if (e.key === "Escape") {72 onClose();73 return;74 }7576 // Focus trap: keep focus within modal77 if (e.key === "Tab" && modalRef.current) {78 const focusableElements = Array.from(79 modalRef.current.querySelectorAll<HTMLElement>(80 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'81 )82 );83 const [firstElement] = focusableElements;84 const lastElement = focusableElements.at(-1);8586 if (e.shiftKey) {87 // Shift + Tab88// … truncated
What it pulls in
npm packages
Files it writes
More from SmoothUI Registry
All 178 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Agent Avataragent-avatar | SmoothUI Registry | Components | Free | no deps | |
| Ai Approvalai-approval | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Artifactai-artifact | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Branchai-branch | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Citationai-citation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Context Meterai-context-meter | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Conversationai-conversation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Coreai-core | SmoothUI Registry | Components | Free | 1 dep |
