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

Next.js 14 + Framer Motion页面过渡失效及警告问题求助

解决Next.js v14 + Framer Motion页面过渡动画失效及上下文警告问题

1. 动画失效的根源与修复

动画不生效的核心原因是根layout.tsx默认为服务端渲染(SSR),而Framer Motion的AnimatePresence、motion组件需要在客户端环境中运行才能跟踪路由变化、执行动画逻辑。

修复方案:拆分客户端组件隔离动画逻辑

保持根layout.tsx的SSR特性(不影响SEO与初始加载性能),将路由内容的动画包裹逻辑抽离为单独的客户端组件:

根layout.tsx(SSR)

// app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import ClientLayout from './ClientLayout'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Your App Title',
  description: 'Your app description',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        {/* 用客户端组件包裹所有页面内容 */}
        <ClientLayout>{children}</ClientLayout>
      </body>
    </html>
  )
}

客户端动画组件ClientLayout.tsx

// app/ClientLayout.tsx
'use client'

import { AnimatePresence, motion } from 'framer-motion'
import { usePathname } from 'next/navigation'

export default function ClientLayout({ children }: { children: React.ReactNode }) {
  // 获取当前路由路径,作为动画触发的唯一标识
  const pathname = usePathname()

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.5 }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

关键说明

  • 'use client'指令强制组件在客户端渲染,确保Framer Motion的API能正常运行。
  • usePathname()获取路由路径作为motion.div的key,路由变化时AnimatePresence会检测到key变更,自动触发退出、进入动画。

2. 解决“多个渲染器同时渲染同一上下文提供者”警告

该警告通常是由于Framer Motion的上下文(如FrozenRouter)被重复嵌套,或在SSR与客户端同时渲染导致的。

修复方案:规范FrozenRouter的使用

如果必须使用FrozenRouter(仅适用于特定场景,Next.js 14 App Router多数情况下无需依赖它),需满足以下要求:

  • 仅在客户端组件内使用,且只包裹一次。
  • 避免与Next.js原生路由上下文重复嵌套。

修改后的ClientLayout.tsx示例:

// app/ClientLayout.tsx
'use client'

import { AnimatePresence, motion, FrozenRouter } from 'framer-motion'
import { usePathname } from 'next/navigation'

export default function ClientLayout({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()

  return (
    {/* 仅在客户端组件最外层包裹一次FrozenRouter */}
    <FrozenRouter>
      <AnimatePresence mode="wait">
        <motion.div
          key={pathname}
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -20 }}
          transition={{ duration: 0.5 }}
        >
          {children}
        </motion.div>
      </AnimatePresence>
    </FrozenRouter>
  )
}

额外检查项

  • 移除页面或子组件中重复的AnimatePresence或FrozenRouter嵌套。
  • 确保所有Framer Motion相关组件(motion系列、AnimatePresence)都仅在客户端组件内使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 23:07:29