Next.js 13.4(App Router)中AnimatePresence的正确放置位置
在Next.js 13.4 App Router中正确使用Framer Motion的AnimatePresence
你之前的写法无法生效主要有两个核心原因:一是RootLayout默认是服务器组件,不能直接使用Framer Motion这类客户端组件;二是App Router下无法直接给children添加key来触发AnimatePresence的路由变化检测。以下是可行的解决方案:
步骤1:创建客户端动画布局组件
新建一个带'use client'指令的组件,专门处理AnimatePresence的包裹逻辑:
'use client'; import { AnimatePresence } from 'framer-motion'; import { usePathname } from 'next/navigation'; export default function AnimatedLayout({ children }: { children: React.ReactNode }) { // 用App Router的usePathname获取当前路由路径,替代旧版的router.asPath const pathname = usePathname(); return ( // initial={false} 保持和旧版一致,避免初始加载触发动画 // mode="wait" 确保退出动画完成后再渲染新页面 <AnimatePresence initial={false} mode="wait"> {/* 用当前路径作为key,让AnimatePresence识别路由变化 */} <div key={pathname}>{children}</div> </AnimatePresence> ); }
步骤2:修改RootLayout使用动画组件
在RootLayout中引入刚才创建的AnimatedLayout,用它包裹路由内容:
import AnimatedLayout from './AnimatedLayout'; import Footer from './Footer'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@48,400,1,0" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> </head> <body> {/* 用AnimatedLayout替代直接包裹children */} <AnimatedLayout>{children}</AnimatedLayout> <div className="container__small"> <Footer /> </div> </body> </html> ); }
步骤3:给页面组件添加动画属性
确保每个页面组件(放在app/下的路由组件)使用motion组件,并设置initial、animate、exit动画参数:
'use client'; import { motion } from 'framer-motion'; export default function Home() { return ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} > <h1>首页内容</h1> </motion.div> ); }
关键说明
'use client'指令是必须的:Framer Motion的组件依赖客户端环境,所以包裹AnimatePresence的组件必须声明为客户端组件。usePathname的作用:替代旧版useRouter的asPath,用来标识当前路由,让AnimatePresence能检测到页面切换。mode="wait":防止新页面提前渲染导致的视觉冲突,保证退出动画完成后再显示新页面。
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

