Dialog

An animated dialog component powered by Framer Motion, offering smooth transitions and interactive visual effects for modal windows

Installation

npm install framer-motion
modal.tsx
1
import React, {
2
createContext,
3
useContext,
4
useState,
5
useEffect,
6
ReactNode,
7
} from 'react';
8
import { AnimatePresence, motion } from 'framer-motion';
9
import { X } from 'lucide-react';
10
11
interface ModalContextProps {
12
open: boolean;
13
setOpen: (open: boolean) => void;
14
}
15
16
const ModalContext = createContext<ModalContextProps | undefined>(undefined);
17
18
const useModal = () => {
19
const context = useContext(ModalContext);
20
if (!context) {
21
throw new Error('useModal must be used within a ModalProvider');
22
}
23
return context;
24
};
25
26
interface FramerModalProps {
27
children: ReactNode;
28
open?: boolean;
29
setOpen?: (open: boolean) => void;
30
}
31
32
export function FramerModal({
33
children,
34
open: controlledOpen,
35
setOpen: controlledSetOpen,
36
}: FramerModalProps) {
37
const [internalOpen, setInternalOpen] = useState(false);
38
const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
39
const setOpen =
40
controlledSetOpen !== undefined ? controlledSetOpen : setInternalOpen;
41
useEffect(() => {
42
if (open) {
43
document.body.classList.add('overflow-hidden');
44
} else {
45
document.body.classList.remove('overflow-hidden');
46
}
47
48
const handleKeyDown = (event: KeyboardEvent) => {
49
if (event.key === 'Escape') {
50
setOpen(false);
51
}
52
};
53
54
document.addEventListener('keydown', handleKeyDown);
55
return () => {
56
document.removeEventListener('keydown', handleKeyDown);
57
};
58
}, [open]);
59
return (
60
<ModalContext.Provider value={{ open, setOpen }}>
61
<AnimatePresence>
62
{open && (
63
<motion.div
64
initial={{ opacity: 0 }}
65
animate={{ opacity: 1 }}
66
exit={{ opacity: 0 }}
67
className='fixed inset-0 z-20 top-0 left-0 right-0 bottom-0 flex flex-col items-center w-full h-screen justify-center dark:bg-black/90 bg-white/90 backdrop-blur-sm cursor-zoom-out border'
68
onClick={() => setOpen(false)}
69
>
70
<motion.div
71
initial={{ opacity: 0, y: 8 }}
72
animate={{ opacity: 1, y: 0 }}
73
exit={{ opacity: 0, y: 8 }}
74
onClick={(e) => e.stopPropagation()}
75
className=' w-full max-w-md rounded-xl bg-white/5 p-6 backdrop-blur-2xl border'
76
>
77
<button
78
className='absolute top-2 right-2'
79
onClick={() => setOpen(false)}
80
>
81
<X />
82
</button>
83
{children}
84
</motion.div>
85
</motion.div>
86
)}
87
</AnimatePresence>
88
</ModalContext.Provider>
89
);
90
}
91
92
export function ModalContent({ children }: { children: ReactNode }) {
93
return <>{children}</>;
94
}

Props

FramerModal Props

PropTypeDefaultDescription
childrenReact.ReactNode-The content inside the modal.
openbooleanundefinedControls the visibility of the modal.
setOpen(open: boolean) => voidundefinedFunction to set the modal's visibility state.

ModalContent Props

PropTypeDefaultDescription
childrenReact.ReactNode-The content inside the modal content area.