【Next.js】Framer Motion(motion)の使い方|入場とスクロールをコード例で解説
公開日: 2026年9月12日 · 読了目安 13 分
Next.js の App Router で Framer Motion(motion/react)を使う手順をコード付きで解説。"use client" の置き場所、順番に出る入場、whileInView、useScroll の背景移動、よくあるビルドエラーの直し方までまとめます。
要点: motion を入れ、"use client" を付けた部品の中で "motion/react" から読み込みます。ページの残りはサーバーコンポーネントのままにして、動く部品だけをそこから呼び出します。
Next.js で Framer Motion を使うときの基本の形
Next.js の App Router で Motion(旧 Framer Motion)を入れたら、createContext のエラーが出た。page.tsx の先頭に "use client" を書いたら動いたが、本当にこれでよいのか。
そんな迷いからこの記事にたどり着いた人は多いはずです。答えはシンプルです。
ページ本体は、サーバーコンポーネント(サーバーで組み立てる部品)のままにします。
動かしたい部品だけを別ファイルに切り出します。そのファイルの先頭にだけ "use client"(この部品はブラウザで動かす、という印)を書きます。
ヒーローや CTA など、動きが必要な場所に絞って Motion を使います。
この記事で分かることは次のとおりです。
framer-motion と motion の違いと、どちらを入れるか
ブラウザで動かす印を置く場所と、ページ本体から呼び出す形
見出し・本文・ボタンを順番に出す入場アニメーション
画面に入ったときに一度だけ動かす方法
スクロールに合わせて背景だけを動かす方法
よくあるビルドエラーと、動きを減らす設定への対応
Framer Motion の部品だけをクライアントにする理由
Motion のコンポーネントは、ブラウザで動く React の機能(状態やコンテキスト)を使います。そのため、サーバーで組み立てる部品の中では直接動きません。
ここまでは多くの人が知っているところです。問題は、エラーを消すためにページ本体まで丸ごとブラウザ向けにしてしまうことです。
そうすると、そのページで使う子コンポーネントもすべてブラウザ向けの JavaScript に含まれます。データの取得やメタ情報の生成も、サーバー側の利点を失いやすくなります。
動く部品だけを切り出せば、ブラウザに送るコードは小さく保てます。レビューするときも「動きの責任はこのファイル」と範囲がはっきりします。
AI エージェントに修正を頼むときも、渡すファイルが少ないほど指示がぶれません。
なお、ふわっと出るだけの入場なら CSS の animation でも作れます。Motion を足すと手間に見合うのは、次のような場面です。
スクロール量に合わせて動かしたい
画面に入った瞬間に一度だけ動かしたい
複数の要素を順番に出したい
framer-motion と motion/react はどちらを入れるか
Framer Motion は現在「Motion」という名前で開発が続いています。新しいパッケージ名は motion で、React からは motion/react を import(読み込み)します。
従来の framer-motion パッケージも引き続き使えます。そのため、検索すると両方の書き方が出てきます。
新しく始めるなら、新しい名前のパッケージを入れて読み込み元もそろえるのがおすすめです。コピーしてきたコードが旧名から読み込んでいる場合は、旧名のパッケージをそのまま入れても動きます。
大事なのは、読み込み元をどちらか一方にそろえることです。
motion は内部で framer-motion を使っています。別々のバージョンを入れると重複してバンドルされたり、MotionConfig などの設定が共有されなかったりします。
# 新しく始めるなら
npm install motion
# 既存コードが framer-motion を import しているなら
npm install framer-motion動かすファイルだけに "use client" を書く
動く要素(コードの motion.h1 など)を使うファイルの先頭に、この1行を書きます。ページ本体からは、普通の部品として呼び出すだけです。
"use client";
import { motion } from "motion/react";
export function HeroTitle({ children }: { children: React.ReactNode }) {
return (
<motion.h1
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
className="text-4xl font-semibold tracking-tight md:text-6xl"
>
{children}
</motion.h1>
);
}ページ本体はサーバー側のまま動く部品を呼び出す
ページ本体には、ブラウザで動かす印を書きません。データの取得やページ情報(metadata)の定義はここに残し、動く部分だけを子の部品に任せます。
import { HeroTitle } from "@/components/HeroTitle";
export default function Page() {
return (
<main className="mx-auto max-w-5xl px-6 py-24">
<HeroTitle>Next.js で作る、動きのある LP</HeroTitle>
<p className="mt-6 text-lg text-zinc-600">説明文はサーバー側で描画されます。</p>
</main>
);
}Framer Motion で入場アニメーションを順番に出す
見出しと本文とボタンを同じタイミングで出すと、動いているのに印象に残りません。目に留まる入場は、要素ごとに少しずつ遅らせた「段差」から生まれます。
Motion では、親の要素に variants を書きます。これは「隠れた状態」と「見えた状態」に名前を付けてまとめる設定です。
あわせて staggerChildren(子を何秒ずつずらして出すか)を指定すると、子が順番に表示されます。遅らせる間隔は 0.08〜0.16 秒が目安です。
イージングは最後にゆっくり止まる ease-out 寄りにすると、LP のテンポが崩れにくくなります。
"use client";
import { motion, type Variants } from "motion/react";
const container: Variants = {
hidden: {},
show: { transition: { staggerChildren: 0.12, delayChildren: 0.1 } },
};
const item: Variants = {
hidden: { opacity: 0, y: 16 },
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] } },
};
export function HeroIntro() {
return (
<motion.div variants={container} initial="hidden" animate="show" className="space-y-6">
<motion.h1 variants={item} className="text-5xl font-semibold">見出し</motion.h1>
<motion.p variants={item} className="text-lg text-zinc-600">本文の説明</motion.p>
<motion.a variants={item} href="/signup" className="inline-block rounded-full bg-black px-6 py-3 text-white">
無料で始める
</motion.a>
</motion.div>
);
}入場アニメーションでも画像は最初から見せておく
ヒーローの大きな画像を透明度 0 から出すと、ページで一番大きな要素の表示が遅れます。これは LCP(ページの主な内容が表示されるまでの時間)の悪化につながります。
画像は最初から見せておき、文字だけを動かすと安全です。画像も動かすなら、透明度を 0.6 程度から上げる形にします。
Framer Motion で画面に入ったときに一度だけ動かす
ページの途中にあるセクションは、読み込み時ではなく、スクロールして画面に入ったときに動かします。whileInView(画面に入ったら動かす指定)を使うと、要素が見えた瞬間にアニメーションが始まります。
一度表示したあとに同じ動きを繰り返さないようにするには、viewport の once を true にします。一度動いたあとは監視をやめるので、上に戻るたびに動きが繰り返されることもありません。
amount は「要素の何割が見えたら動かすか」を表します。
"use client";
import { motion } from "motion/react";
export function FadeInSection({ children }: { children: React.ReactNode }) {
return (
<motion.section
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, ease: "easeOut" }}
>
{children}
</motion.section>
);
}Framer Motion でスクロールに合わせて背景を動かす
スクロール量に連動させたいときは、useScroll と useTransform を組み合わせます。
useScroll は、スクロール量を読み取る仕組みです。target に要素を渡すと、その要素が画面を通過する進み具合が 0〜1 の値で取れます。
その値を、useTransform という値を変換する仕組みで移動量に変えます。
動かすのは背景画像だけにして、移動量は数パーセントに抑えてください。文字まで追従させると読みにくくなります。
背景を少し大きめに置いておくと、動いたときに端の隙間が見えません。
"use client";
import { useRef } from "react";
import { motion, useScroll, useTransform } from "motion/react";
export function ParallaxHero() {
const ref = useRef<HTMLElement>(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start start", "end start"],
});
const y = useTransform(scrollYProgress, [0, 1], ["0%", "15%"]);
return (
<section ref={ref} className="relative h-[80vh] overflow-hidden">
<motion.div
style={{ y, backgroundImage: "url(/hero.jpg)" }}
className="absolute inset-x-0 -top-[10%] h-[120%] bg-cover bg-center"
/>
<div className="relative z-10 flex h-full items-center justify-center">
<h1 className="text-5xl font-semibold text-white">見出しは動かさない</h1>
</div>
</section>
);
}Framer Motion を「動きを減らす」設定に対応させる
OS の設定で「視差効果を減らす」「動きを減らす」を選んでいる人がいます。CSS では prefers-reduced-motion で判定する設定です。
Motion では MotionConfig の reducedMotion="user" を使います。この設定に合わせて移動の動きを止め、透明度の変化だけを残せます。
レイアウトの近くに1つ置けば、その中の動く要素すべてに効きます。情報が動きに頼らないようにすることも、あわせて確認してください。
"use client";
import { MotionConfig } from "motion/react";
export function MotionProvider({ children }: { children: React.ReactNode }) {
return <MotionConfig reducedMotion="user">{children}</MotionConfig>;
}Framer Motion でよくある失敗とビルドエラーの直し方
Next.js と Motion の組み合わせでつまずく場面は、ほぼ決まっています。エラー文が出たら、まず次の4つを疑ってください。
createContext only works in Client Components などのエラーが出る
使っているバージョンによってエラー文は変わりますが、原因はどれも同じです。
動く部品のファイルに、ブラウザで動かす印("use client")がありません。ページ本体ではなく、動きのライブラリを読み込んでいるファイルの先頭に書きます。
Module not found: Can't resolve 'framer-motion' と出る
パッケージが入っていないか、読み込み元と入れたパッケージが食い違っています。
framer-motion を一度も入れていないのに、コードが framer-motion から読み込んでいる場合によく起きます。pnpm のように、直接入れたパッケージしか読み込めない構成でも起きます。
import を motion/react に直すか、framer-motion を入れてください。
ページ全体をブラウザで動く部品にしてしまう
エラーは消えますが、ページ全体がブラウザ向けのコードになります。ブラウザで動く部品では、ページ情報(metadata)を書き出すこともできません。
動く部品だけを切り出す形に戻してください。
全セクションに同じ強さの動きを付ける
すべてが動くと、どこが大事なのか伝わりません。強い動きはヒーローと最後の CTA に絞り、それ以外は薄いフェードで十分です。
完成した Framer Motion のセクションをコピーして使う方法

イージングや遅延を自分の手で細かく決めたいなら、この記事のコードを土台に組むのが確実です。
一方で、ページの雰囲気を先に決めたい場合もあります。既存の shadcn/ui のページにセクションを足したいときも同じです。
そうした場合は、動きが完成したセクションを載せる方が早く進みます。
DesignLayer は、人が組んだ React + Tailwind のモーション付きセクションを、ブラウザでライブプレビューできるカタログです。
閲覧は無料で、カードの登録もいりません。https://design-layer.com/ja/components
Copy for Cursor を使うと、統合の指示・ライブプレビューの URL・本体の TSX・npm 依存をまとめてコピーできます。無料サンプル10個を無料アカウントで回数制限なくコピーでき、動画プロンプトは月1回です。Free では MCP に接続できません。カタログ全体は30日ビルドパス($19の1回払い、自動更新なし)、Lifetime Access(先着100名の$79の1回払い)、Plus(月額 $9、いつでも解約可)です。
Cursor での使い方は https://design-layer.com/ja/articles/cursor-react-motion-components-guide で紹介しています。
ホームプロジェクト・スタック は、画面に入ると作品カードが下からフェードし、スクロールに合わせて重なりながら入れ替わる
ポートフォリオタイプ・デッキ は、スクロールの進みに合わせてカードが横から滑り込む
ワークス・エキスパンダ は、セクションが見えたときに画像の帯が左から入り、選んだ画像が横に広がる
無料アカウントは無料サンプル10個を無料アカウントで回数制限なくコピーでき、動画プロンプトは月1回です。Free では MCP に接続できません。カタログ全体は30日ビルドパス($19の1回払い、自動更新なし)、Lifetime Access(先着100名の$79の1回払い)、Plus(月額 $9、いつでも解約可)です。
Next.js の Framer Motion についてよくある質問
Next.js で Motion を導入するときに、よく出る質問をまとめました。
同じライブラリの旧パッケージ名と新パッケージ名です。新しく始めるなら motion を入れて、motion/react から読み込みます。
既存のコードが framer-motion なら、そのままでも動きます。読み込み元をどちらか一方にそろえることだけ守ってください。
まとめ:Next.js で Framer Motion を使う手順
ページ本体はサーバー側のまま、動く部品のファイルだけに "use client" を書く
新しく始めるなら motion を入れ、読み込み元をそろえる
入場は見出しや本文を 0.08〜0.16 秒ずつずらして出す
ページ途中のセクションは、画面に入ったときに一度だけ動かす
スクロール連動は、背景だけを数パーセント動かす
「動きを減らす」設定の人には、移動の動きを止める
まずはヒーローの見出しを1つ切り出して、順番に出る入場から試してください。ヒーロー全体の組み立ては https://design-layer.com/ja/articles/nextjs-animated-hero-section-guide 、Tailwind CSS だけで作る動きは https://design-layer.com/ja/articles/nextjs-tailwind-animation-guide で解説しています。





