Animated Dialog

An animated dialog component powered by Framer Motion, featuring smooth transitions and interactive visual effects for modal windows. Perfect for creating engaging overlays, popups, and interactive dialogs with modern animations.

Installation

npm install motion
modal.tsx
1
import React, {
2
createContext,
3
useContext,
4
useState,
5
useEffect,
6
ReactNode,
7
} from 'react';
8
import { AnimatePresence, motion } from 'motion/react';
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.