Morphing Dialog
Dialog that expands from its trigger using framer-motion layoutId shared layout animation. Smooth morph in/out.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Morphing DialogDialog that expands from its trigger using framer-motion layoutId shared layout animation. Smooth morph in/out.
"use client";
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
type MorphingDialogProps = {
trigger: React.ReactNode;
children: React.ReactNode;
};
export function MorphingDialog({ trigger, children }: MorphingDialogProps) {
const [open, setOpen] = useState(false);
return (
<>
<motion.div
layoutId="mp-morphing-dialog"
className="mp-morphing-dialog-trigger"
onClick={() => setOpen(true)}
style={{ cursor: "pointer" }}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{trigger}
</motion.div>
<AnimatePresence>
{open && (
<>
<motion.div
className="mp-morphing-dialog-backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.18 }}
onClick={() => setOpen(false)}
/>
<motion.div
layoutId="mp-morphing-dialog"
className="mp-morphing-dialog-modal"
>
{children}
<button
className="mp-morphing-dialog-close"
onClick={() => setOpen(false)}
aria-label="Close"
>
✕
</button>
</motion.div>
</>
)}
</AnimatePresence>
</>
);
}Install
npm install framer-motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| triggerrequired | ReactNode | — | Clickable trigger element |
| childrenrequired | ReactNode | — | Dialog content |