You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next JS 13 App Router中AnimatePresence退出动画失效求助

Next.js 13 App Router 中 Framer Motion 退出动画无效的解决方案

问题描述

基于 Next.js 13 新 App Router 的项目中,使用 Framer Motion 的 AnimatePresence 组件时,页面进入动画正常触发,但退出动画完全无效。已尝试在 RootLayout 和 template.tsx 中配置 AnimatePresence mode="wait",但问题仍未解决。

项目目录结构

app
  - layout.tsx
  - page.tsx
  - projects
  -- project
  --- page.tsx

已尝试的操作

  • 在 app/layout.tsx 中包裹 AnimatePresence:
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html lang="en">
          <body className={inter.className}>
            <AnimatePresence mode="wait">{children}</AnimatePresence>
          </body>
        </html>
      );
    }
    
  • 为首页添加 motion 动画配置:
    const Home = () => {
      return (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1, transition: { duration: 3 } }}
          exit={{ opacity: 0, transition: { duration: 5 } }}
    className="grid gap-4 grid-cols-[repeat(1,minmax(400px,_1fr))] grid-rows-[repeat(4,minmax(400px,_1fr))] items-center justify-center"
        >
          <Link href="/projects/project">project page</Link>
        </motion.div>
      );
    };
    
  • 为项目详情页添加相同逻辑的动画配置;
  • 按照 Next.js 文档建议改用 app/template.tsx 包裹 AnimatePresence:
    const Template: FC<{ children: ReactNode }> = ({ children }) => {
      return (
        <AnimatePresence mode="wait">{children}</AnimatePresence>
      );
    };
    

解决方案

1. 必须使用 template.tsx 并声明为客户端组件

RootLayout 是持久化渲染的,不会随路由切换重新渲染,因此无法让 AnimatePresence 检测到页面组件的卸载。必须使用 template.tsx,且需要添加 'use client' 声明(因为 Framer Motion 是客户端组件):

'use client';
import { AnimatePresence } from 'framer-motion';
import { ReactNode } from 'react';

export default function Template({ children }: { children: ReactNode }) {
  return <AnimatePresence mode="wait">{children}</AnimatePresence>;
}

2. 给每个页面的 motion 组件添加唯一 key

AnimatePresence 需要通过唯一 key 识别组件的挂载/卸载状态,可使用 usePathname() 获取当前路由路径作为 key:

'use client';
import { motion } from 'framer-motion';
import Link from 'next/link';
import { usePathname } from 'next/navigation';

// 首页示例
export default function Home() {
  const pathname = usePathname();
  return (
    <motion.div
      key={pathname}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1, transition: { duration: 3 } }}
      exit={{ opacity: 0, transition: { duration: 5 } }}
      className="grid gap-4 grid-cols-[repeat(1,minmax(400px,_1fr))] grid-rows-[repeat(4,minmax(400px,_1fr))] items-center justify-center"
    >
      <Link href="/projects/project">project page</Link>
    </motion.div>
  );
}

项目详情页同理,添加 key={pathname} 即可。

3. 确保所有使用 motion 的页面声明为客户端组件

所有包含 motion 组件的页面文件顶部必须添加 'use client',否则无法在客户端触发动画逻辑。

4. 验证版本兼容性

确保使用的 framer-motion 版本为 10.x 及以上,旧版本可能存在 Next.js 13 App Router 的兼容性问题。

内容的提问来源于stack exchange,提问作者Raffi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 03:32:37