Framer Motion Animate Presence在Next.js App Router拦截路由中失效
问题背景
基于Next.js拦截路由实现登录模态框(参考Nextgram示例),但搭配Framer Motion的AnimatePresence时动画完全不生效。尝试将AnimatePresence迁移到app/layout.tsx、app/(auth)/layout.tsx等多个位置,且GitHub旧issue中的解决方案均无效。
可行解决方案
强制拦截路由页面为客户端组件
拦截路由对应的页面组件(app/(features)/@loginModal/(.)login/page.tsx)必须添加"use client";指令,因为Framer Motion的动画逻辑依赖客户端渲染,服务端组件无法处理动态动画状态:"use client"; import Modal from "@General/Modal"; import Login from "@Auth/Login"; export default function NavbarLogin() { return ( <Modal> <Login /> </Modal> ); }为模态框添加唯一
key标识
AnimatePresence通过key识别组件的挂载/卸载状态,拦截路由切换时,需确保模态框拥有稳定的唯一key。可以在布局中克隆loginModal时传入key:<AnimatePresence> {loginModal && React.cloneElement(loginModal, { key: "login-modal" })} </AnimatePresence>或者直接在Modal组件的根元素上绑定key:
// Modal组件示例 "use client"; import { motion } from "framer-motion"; export default function Modal({ children, key = "login-modal" }) { return ( <motion.div key={key} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} transition={{ duration: 0.3 }} className="fixed inset-0 bg-black/50 flex items-center justify-center p-4" > {children} </motion.div> ); }确保AnimatePresence直接包裹动态元素
避免在AnimatePresence和loginModal之间嵌套静态容器组件,保证loginModal是AnimatePresence的直接子元素,且路由切换时该元素会被真实卸载(而非通过CSS隐藏)。升级依赖版本
确保使用的Next.js(≥13.4)和Framer Motion(≥10.16)为最新稳定版,旧版本的App Router与AnimatePresence存在兼容性bug,升级后可解决部分底层问题。临时禁用Strict Mode排查
开发环境下React Strict Mode的双重渲染可能导致exit动画被跳过,可在next.config.js中临时禁用排查:/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: false, }; module.exports = nextConfig;注意:生产环境建议保持Strict Mode开启,此方法仅用于定位问题。
内容的提问来源于stack exchange,提问作者Kartik

