Since Next.js doesn’t support exit animations yet (Relevant issue), we need to manually create the animation event.
For this example I’m using the Sheet component from shadcn/ui, but you can use the Dialog from Radix directly with your own style.
Initial modal component
This is our initial component using shadcn/ui Drawer component, based on the Next.js docs.
"use client";
import { useRouter } from "next/navigation";import { Sheet, SheetContent } from "@/components/ui/sheet";
export default function Modal({ children }: { children: React.ReactNode }) { const router = useRouter();
function handleClose() { router.back(); }
return ( <Sheet open onOpenChange={handleClose}> <SheetContent side="bottom" className="h-full overflow-auto rounded-t-3xl px-0 py-16 md:h-[96%]" > {children} </SheetContent> </Sheet> );}
Where we just call the router.back() to close the modal.
Adding a closing animation
To achieve this, we need to control the open state to perform the router.back() after the closing animation ends, using onAnimationEndCapture.
"use client";
import { useRouter } from "next/navigation";import { Sheet, SheetContent } from "@/components/ui/sheet";import { useState } from "react";
export default function Modal({ children }: { children: React.ReactNode }) { const [open, setOpen] = useState(true); const router = useRouter();
function handleClose() { router.back(); setOpen(false); }
function handleAnimationEnd() { // when the modal animation ends: if it's closed, navigate back if (!open) { router.back(); } }
return ( <Sheet open={open} onOpenChange={handleClose}> <SheetContent onAnimationEndCapture={handleAnimationEnd} side="bottom" className="h-full overflow-auto rounded-t-3xl px-0 py-16 md:h-[96%]" > {children} </SheetContent> </Sheet> );}
Result
Now when pressing the close button or the ESC key, the modal will exit with an animation then go back to the previous URL.
We still can’t animate when the React component is unmounted (eg: when using the browser’s back button), for that we’ll have to wait for native framework support.
For now this cover the rest of usecases.