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

Next.js 13(App Router)封装Framer Motion为可复用组件的问题

在Next.js服务器组件中复用Framer Motion的正确方案

你之前的写法错误在于:返回motion对象的函数组件无法通过<Motion.div />的方式调用——因为函数组件本身没有div这类静态属性,而motion.div是motion对象的属性,不是组件的属性。

下面提供两种可行的实现方案:

方案1:封装常用的单个动效元素

直接导出motion的具体元素,每个元素都是独立的客户端组件:

"use client";
import { motion } from "framer-motion";

// 按需封装你需要的HTML元素
export const MotionDiv = motion.div;
export const MotionButton = motion.button;
export const MotionSection = motion.section;
export const MotionSpan = motion.span;

在服务器组件中直接导入使用:

import { MotionDiv, MotionButton } from "@/components/MotionElements";

export default function ServerPage() {
  return (
    <div className="container mx-auto p-4">
      <MotionDiv
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.6 }}
        className="mb-6"
      >
        服务器组件渲染的动效容器
      </MotionDiv>
      <MotionButton
        whileHover={{ scale: 1.05, backgroundColor: "#3b82f6" }}
        whileTap={{ scale: 0.95 }}
        className="px-6 py-3 bg-blue-500 text-white rounded-lg"
      >
        带交互动效的按钮
      </MotionButton>
    </div>
  );
}

方案2:通用动态标签组件

如果需要更灵活地支持任意HTML标签,可以封装一个能动态指定标签的客户端组件:

"use client";
import { motion, MotionProps } from "framer-motion";

// 定义组件的类型,支持所有HTML标签和Framer Motion的属性
type MotionGenericProps<T extends keyof JSX.IntrinsicElements> = 
  MotionProps & JSX.IntrinsicElements[T] & { as: T };

export function Motion<T extends keyof JSX.IntrinsicElements>({
  as,
  ...props
}: MotionGenericProps<T>) {
  // 根据传入的as属性获取对应的motion元素
  const MotionElement = motion[as];
  return <MotionElement {...props} />;
}

在服务器组件中使用时,通过as指定标签类型:

import { Motion } from "@/components/Motion";

export default function ServerPage() {
  return (
    <div className="space-y-4">
      <Motion
        as="h1"
        initial={{ x: -50 }}
        animate={{ x: 0 }}
        transition={{ type: "spring", stiffness: 100 }}
        className="text-2xl font-bold"
      >
        动态标签的动效标题
      </Motion>
      <Motion
        as="img"
        src="/example-image.jpg"
        alt="示例图片"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ delay: 0.3 }}
        className="w-full h-64 object-cover rounded"
      />
    </div>
  );
}

两种方案的核心都是:将Framer Motion的元素封装在带"use client"指令的客户端组件中,然后在服务器组件中导入使用——这样既遵守了Next.js App Router的组件规则,又能实现动效的复用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 17:17:34