Notification
A simple example of notification that uses framer motion for animations.
Show code
"use client";
import { type DragHandlers } from "framer-motion";
import { useState } from "react";
import { motion, useAnimate, AnimatePresence } from "framer-motion";
import { IconX } from "~/icons/x.icon";
import { IconEmail } from "~/icons/email.icon";
const Notification = () => {
const [scope, animate] = useAnimate();
const [active, setActive] = useState(false);
const handleDragEnd: DragHandlers["onDragEnd"] = (_, info) => {
const offset = info.offset.x;
const velocity = info.velocity.x;
if (offset > 200 || velocity > 500) {
animate(scope.current, { x: "120%" }, { duration: 0.2 });
setTimeout(() => setActive(false), 200);
} else {
animate(scope.current, { x: 0, opacity: 1 }, { duration: 0.5 });
}
};
return (
<div className="relative flex items-center justify-center w-full min-h-28 overflow-hidden">
<motion.button
type="button"
layoutId="notification"
whileTap={{ scale: 0.9 }}
onClick={() => setActive(!active)}
className="bg-white px-4 py-2 text-black rounded-md text-sm font-medium truncate"
>
<motion.span>Send email</motion.span>
</motion.button>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-w-full">
<AnimatePresence>
{active && (
<motion.div
drag="x"
role="alert"
aria-live="polite"
ref={scope}
dragDirectionLock
layoutId="notification"
onDragEnd={handleDragEnd}
dragConstraints={{ left: 0, right: 200 }}
className="bg-white rounded-xl text-black py-3 pl-4 pr-12 relative w-full max-w-full"
>
<motion.div className="flex flex-row gap-3">
<motion.div
exit={{ x: 20, opacity: 0 }}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1, transition: { delay: 0.3 } }}
>
<IconEmail
aria-hidden
className="text-primary size-7 -translate-y-0.5"
/>
</motion.div>
<motion.div className="flex flex-col gap-y-0.5">
<motion.h3
exit={{ x: 20, opacity: 0 }}
initial={{ x: -20, opacity: 0 }}
className="text-sm font-medium truncate"
animate={{ x: 0, opacity: 1, transition: { delay: 0.3 } }}
>
Email sent successfully
</motion.h3>
<motion.p
exit={{ x: 20, opacity: 0 }}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1, transition: { delay: 0.3 } }}
className="text-xs font-medium text-black/60 truncate"
>
Your email has been sent successfully.
</motion.p>
</motion.div>
</motion.div>
<motion.button
type="button"
aria-label="Close notification"
onClick={() => setActive(false)}
whileTap={{ scale: 0.6 }}
exit={{ x: -10, opacity: 0 }}
initial={{ x: 10, opacity: 0 }}
animate={{ x: 0, opacity: 1, transition: { delay: 0.3 } }}
className="text-xs font-medium size-5 absolute top-2 right-2 flex items-center justify-center"
>
<IconX className="grow-0 shrink-0 size-4" aria-hidden />
</motion.button>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
};
export { Notification };