React

Framer Motion(現Motion)とは|motion/reactの使い方とeasing・springの既定値【v13対応】

Framer Motion(現Motion)とは|motion/reactの使い方とeasing・springの既定値【v13対応】

Framer Motionは、Reactでアニメーションを書くためのオープンソースライブラリです。2024年11月12日に開発元のFramer社から独立し、名称が「Motion」に変わりました。npmのパッケージは motion、Reactから読み込むパスは motion/react です。書き方は旧 framer-motion と同じで、2026年9月時点の最新版は両パッケージとも13.4.4です。

この記事では、改称後のパッケージの選び方と motion コンポーネントの基本を押さえます。そのうえで、「transitionを書かなかったときに何が起きるか」を、v13.4.4のソースコードに基づいて説明します。easing(イージング)とspringの設定値、reducedMotionの初期値、バンドルサイズも扱います。

まとめ:Framer Motion(Motion)の要点

  • Framer Motionは2024年11月にMotionへ改称した。新規導入は npm install motion で入れ、import { motion } from "motion/react" と書く。既存の framer-motion も同じ版番号で更新が続いている。
  • transitionを書かない場合、x・rotate・scaleなどの変形はspring、opacityや色は0.3秒のtween(曲線は cubic-bezier(0.25, 0.1, 0.35, 1))で動く。duration だけを書くと、変形もspringではなくeaseOutのtweenになる。
  • ease に文字列で渡せる名前は11種類。3次ベジェの配列、steps()、自作関数も使える。"easeOutCubic" のような未定義の名前は、開発ビルドではエラーになる。
  • springは、stiffness・damping・mass の物理指定か、visualDuration・bounce の時間指定で組む。両方を書くと物理指定が優先される。v13.4.4では、visualDuration だけを書いても時間指定として扱われない。
  • reducedMotion の初期値は "never" で、OSの「視差効果を減らす」設定を尊重しない。MotionConfig reducedMotion="user" を明示する必要がある。

Framer MotionからMotionへの改称とパッケージの関係

作者のMatt Perry氏は2024年11月12日、公式ブログで「Framer Motion is now independent, introducing Motion」と発表しました。同時に、それまで別ライブラリだったMotion One(素のJavaScript向け)と統合しています。以後はReactに加えて素のJavaScriptからも使えるライブラリになり、2025年3月19日にはVue版も公開されました。

Framer(ノーコードのWebサイト制作ツール)とは別の製品です。Framer Motionは、もともとFramer社の中で開発されていました。検索で「Framer 特徴」「フレーマーとは」と調べてこの記事に来た場合、探しているのはサイト制作ツールのほうかもしれません。

framer-motionとmotionの選択基準

npmでは motion パッケージが framer-motion に依存しており、版番号も揃えて公開されています(2026年9月時点でどちらも13.4.4)。どちらを入れても、React向けの中身は同じです。

項目 motion framer-motion
読み込みパス motion/react framer-motion
最新版(2026年9月) 13.4.4 13.4.4
週間DL数(9月18〜24日) 約2,004万 約4,331万
React 18.2以上 18.2以上
ライセンス MIT MIT

週間DL数はnpmのDownloads API(api.npmjs.org/downloads/point/last-week)で取得した値です。framer-motion のダウンロード数には、motion の依存として取得される分も含まれます。この数字は、旧パッケージ名が主流だという根拠にはなりません。新規プロジェクトでは、公式ドキュメントの表記に合わせて motion を選ぶのが無難です。同等バージョンでパッケージ名だけを移行する場合は、パッケージを入れ替えてimport文を書き換えます。古いメジャーバージョンから更新する場合は、公式アップグレードガイドで各版の破壊的変更も確認してください。

npm uninstall framer-motion
npm install motion

// 変更前
import { motion } from "framer-motion"
// 変更後
import { motion } from "motion/react"

v12・v13で変わった点

メジャー版が上がっても、Reactで使うときの破壊的変更は小さく抑えられています。

版 公開日 React利用者への影響
11.11.12 2024-11-12 motion パッケージ追加(改称)
12.0.0 2025-01-20 React向けAPIの破壊的変更なし
13.0.0 2026-08-05 @emotion/is-prop-valid の自動読み込み廃止
13.4.0 2026-09-14 AnimateView(React 19.3のViewTransition)追加

v12で変わったのは素のJavaScript向けAPIです。press・hover・inView のコールバックは、第1引数に対象要素を受け取るようになりました。v13で影響を受けるのは、主にstyled-componentsやEmotionで motion コンポーネントにスタイルを当てている構成です。更新後に、これまで除外されていた独自のpropsがDOMに出力されるようになった場合、公式ガイドは2つの対処を挙げています。1つは <MotionConfig isValidProp={isPropValid}> で @emotion/is-prop-valid を明示的に渡す方法です。もう1つは motion.create(StyledDiv) のように包む順序を逆にし、props転送をスタイリングライブラリ側に任せる方法です。CSS-in-JSとの併用については「Tailwind CSSとEmotionの違い|v4での使い分けと共存」も参考になります。

motionコンポーネントの使い方:initial・animate・exit

motion.div や motion.button は、普通のHTML要素にアニメーション用のpropsを足したコンポーネントです。initial に開始状態、animate に目標の状態を書くと、マウント時とpropsが変わったときにその差分を補間します。

import { motion } from "motion/react"

export function FadeIn() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 16 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.4, ease: "easeOut" }}
    >
      Hello, Motion
    </motion.div>
  )
}

ホバーやタップの反応は whileHover・whileTap に書きます。要素が消えるときのアニメーションは、AnimatePresence で包んだうえで exit に書きます。AnimatePresence がないと、Reactが要素を即座に取り除くため、exit は再生されません。

import { motion, AnimatePresence } from "motion/react"
import { useState } from "react"

export function Toggle() {
  const [open, setOpen] = useState(false)
  return (
    <>
      <motion.button
        whileHover={{ scale: 1.05 }}
        whileTap={{ scale: 0.95 }}
        onClick={() => setOpen(!open)}
      >
        開閉
      </motion.button>
      <AnimatePresence>
        {open && (
          <motion.div key="panel" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>開閉するパネル</motion.div>
        )}
      </AnimatePresence>
    </>
  )
}

Next.js App Routerでの読み込み方

公式のインストール手順によると、App Routerでは2通りの書き方があります。1つは、読み込むファイルの先頭に "use client" を書いてクライアントコンポーネントにする方法です。もう1つは、Server Componentのまま import * as motion from "motion/react-client" で読み込む方法です。後者のほうが、クライアントへ送るJavaScriptを減らせます。

import * as motion from "motion/react-client"

export default function Page() {
  return <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
}

initial の値はサーバー側のHTMLにも出力されます。ランダムな値や window のサイズから initial を計算すると、サーバーとクライアントで値がずれ、ハイドレーションの不一致が起きます。原因の切り分け方は「ハイドレーションエラー(Hydration Error)とは?原因とReact / Next.jsでの対処法を解説」で扱っています。

transition省略時の既定値:プロパティ別のアニメーション設定

公式ドキュメントが説明しているのは、「xやscaleのような物理的な値はspring、opacityや色はtween」という大枠までです。具体的な数値は、v13.4.4のソースコード(default-transitions.ts)を読むと分かります。アニメーションさせる値の種類ごとに、次の設定が選ばれます。アニメーションさせる値の種類ごとに次の設定が選ばれます。

対象の値 種類 既定の設定
x・y・rotate など(scale以外の変形) spring stiffness 500 / damping 25
scale・scaleX・scaleY spring stiffness 550 / damping 30
opacity・色・width など tween 0.3秒 / [0.25, 0.1, 0.35, 1]
キーフレームが3つ以上 tween 0.8秒 / easeOut

scaleの目標値が0のときは、dampingが 2 × √550(約46.9)の臨界減衰になり、行き過ぎずに縮みきります。opacityなどに使われる曲線について、ソースには「ブラウザ既定のeaseを少し浅くしたもの」というコメントがあります。CSSの ease は cubic-bezier(0.25, 0.1, 0.25, 1) です。

duration単独指定によるtweenへの切り替え

実装でつまずきやすいのは、既定値の切り替え条件です。transition に delay・repeat・when などの順序制御のキーだけが入っている場合、上の表の既定値がそのまま使われます。それ以外のキーが1つでもあると、表の既定値は使われません。そのうえで type も ease もなければ、easeOutのtweenになります。

そのため、x に transition={{ duration: 0.5 }} とだけ書くと、springの揺れがなくなり、0.5秒のeaseOutで止まります。「durationを足したら動きが硬くなった」ときは、springからtweenへの切り替えを確認してください。時間を指定しつつspringの質感を残したい場合は、type: "spring" と visualDuration を組み合わせます(後述)。

easing(ease)の指定方法:名前・ベジェ・steps・関数

tweenの加速・減速の曲線は transition の ease で指定します。渡せるのは、組み込みの名前、4つの数値の配列(3次ベジェ)、0〜1の進捗を受け取って値を返す関数のいずれかです。曲線そのものの仕組みは「ベジェ曲線とは?仕組みをわかりやすく解説|数式・Illustratorでの描き方・スプラインとの違い」を参照してください。

組み込みのeasing名11種と曲線の値

名前で指定できるのは、ソース(easing/utils/map.ts)の easingLookup に登録された11種類です。下の表の「50%時点」は、[email protected] が公開している関数に進捗0.5を渡し、小数第3位に丸めた出力で、Node.jsで実行して確かめました。

名前 定義 50%時点
linear そのまま 0.500
easeIn [0.42, 0, 1, 1] 0.315
easeOut [0, 0, 0.58, 1] 0.685
easeInOut [0.42, 0, 0.58, 1] 0.500
circIn / circOut / circInOut 円弧型 circOut 0.866
backIn / backOut / backInOut backOut [0.33, 1.53, 0.69, 0.99] backOut 1.067
anticipate 一度戻ってから進む 0.500

backOutは最大で約1.084まで行き過ぎてから戻ります。anticipateは、開始直後に約-0.042まで逆方向へ振れます。どちらも、ボタンやトーストのように「弾み」が意味を持つ部品に向いています。一方、スクロール位置や数値の表示に使うと、目標を超えた値が一瞬表示されてしまいます。

CSSでおなじみの "ease" や、他のライブラリにある "easeOutCubic" はこの一覧にありません。TypeScriptでは型エラー(TS2322)になり、JavaScriptでも開発ビルドでは「Invalid easing type」というエラーが投げられます。本番ビルドでは、このチェックが外れます。13.4.2では、キーフレーム補間が未知の名前でクラッシュしないようにeaseInOutへフォールバックする修正が入りました(CHANGELOGの記載は「Unrecognised easing names no longer throw」)。

ベジェ配列・キーフレームごとのease・steps・自作関数

import { motion, cubicBezier, steps } from "motion/react"

// 3次ベジェを配列で直接指定
<motion.div animate={{ x: 200 }} transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }} />

// キーフレームの区間ごとに別のeaseを当てる(区間数=キーフレーム数-1)
<motion.div
  animate={{ x: [0, 120, 80] }}
  transition={{ duration: 1, times: [0, 0.6, 1], ease: ["easeIn", "backOut"] }}
/>

// 4段階でカクカク進める
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} style={{ width: 80, height: 80, backgroundColor: "tomato" }} transition={{ duration: 1, ease: steps(4) }} />

// 関数を渡す(cubicBezierで作った関数も同じ扱い)
<motion.div animate={{ x: 100 }} transition={{ duration: 0.5, ease: cubicBezier(0.65, 0, 0.35, 1) }} />
<motion.div animate={{ x: 100 }} transition={{ duration: 0.5, ease: (t: number) => t * t }} />

ease にeasingの配列を渡すと、キーフレームの各区間に1つずつ割り当てられます。times は各キーフレームの位置を0〜1で指定するもので、上の例では60%の時点で120に達します。steps(4) は進捗を4段階に丸めます。第2引数の既定は "end" で、各段の終わりで次の値へ切り替わります。

springの設定:物理パラメータと時間指定

type: "spring" を指定すると、補間ではなくばねの物理シミュレーションで動きます。組み方は2通りあります。ソース(generators/spring.ts)の getSpringOptions では、stiffness・damping・mass のどれかがあると物理指定として扱われ、duration・bounce は無視されます。

指定方法 キー 省略時・注意
物理指定 stiffness / damping / mass 100 / 10 / 1
時間指定 visualDuration + bounce bounceを併記して使う
時間指定(旧来) duration / bounce 0.8秒 / 0.3
// 見た目上0.4秒で目標に届き、少しだけ弾む
<motion.div animate={{ x: 100 }} transition={{ type: "spring", visualDuration: 0.4, bounce: 0.25 }} />

// 物理パラメータで直接指定
<motion.div animate={{ x: 100 }} transition={{ type: "spring", stiffness: 300, damping: 20, mass: 1 }} />

visualDuration は、揺れが収まるまでではなく、目標に見かけ上届くまでの秒数です。ソースでは stiffness =(2π ÷(visualDuration × 1.2))²、damping = 2 × clamp(0.05, 1, 1 − bounce) × √stiffness に換算しています。clampは値を0.05以上1以下に制限する処理です。bounceを0にすると臨界減衰になり、行き過ぎません。

旧来の duration 指定でbounceを省略した場合、公式ドキュメントの spring のページは既定値を0.25と記載しています。一方、v13.4.4のソースの既定値は0.3です。spring 関数に0.8秒のdurationだけを渡して実行すると、出力は bounce: 0.3 を指定した場合と一致しました。動きを揃えたい場合は、bounceを省略せずに書いてください。

visualDurationの時間指定とbounce併記の条件

v13.4.4の getSpringOptions は、duration か bounce が書かれているときだけ時間指定と判定します。visualDuration だけを書いた場合は判定から漏れ、物理指定の既定値(stiffness 100・damping 10)で動きます。motion パッケージが公開している spring 関数をNode.jsで実行したところ、{ visualDuration: 0.4 } の出力は何も指定しない場合と一致し、最大で目標の116%まで行き過ぎました。一方、{ visualDuration: 0.4, bounce: 0 } では行き過ぎはなく、0.4秒の時点で目標の96.7%に届きました。時間で合わせたいときは、bounce を必ず併記してください。

type: "spring" だけを書いた場合も同じ既定値(100・10)になります。x・yの既定値(500・25)と比べると固有振動数は約2.2分の1(√100 と √500 の比)です。何も書かないときより遅く、大きく揺れる動きになります。

delay・repeat・staggerによるタイミング制御

開始を遅らせるには delay(秒)を使います。repeat には回数か Infinity を指定し、repeatType で "loop"(既定)・"reverse"・"mirror" を選びます。値ごとに別のtransitionを当てたいときは、値の名前をキーにして入れ子にします。

<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} style={{ width: 80, height: 80, backgroundColor: "tomato" }} transition={{ delay: 0.3, duration: 0.5 }} />

<motion.div animate={{ rotate: 180 }} transition={{ repeat: Infinity, repeatType: "reverse", duration: 2 }} />

<motion.div
  animate={{ x: 100, opacity: 1 }}
  transition={{ x: { type: "spring", bounce: 0.3 }, opacity: { duration: 0.2, delay: 0.1 } }}
/>

リストの項目を順番に表示するときは、親と子に variants を持たせます。v13の型定義では staggerChildren に @deprecated が付いており、代わりに delayChildren: stagger(間隔) と書くよう案内されています。stagger の from オプションを使うと、最後の要素や中央から順に動かせます。

import { motion, stagger } from "motion/react"

const list = { hidden: {}, show: { transition: { delayChildren: stagger(0.08) } } }
const item = { hidden: { opacity: 0, y: 8 }, show: { opacity: 1, y: 0 } }

export function List({ items }: { items: string[] }) {
  return (
    <motion.ul variants={list} initial="hidden" animate="show">
      {items.map((t) => (
        <motion.li key={t} variants={item}>{t}</motion.li>
      ))}
    </motion.ul>
  )
}

ドラッグとスクロール連動

drag に "x" か "y" を渡すと、その軸だけでドラッグできます。dragConstraints には移動範囲を数値で渡すか、親要素のrefを渡します。画面内に入ったときに再生するには whileInView を使います。viewport の once: true で再生を1回に限り、amount で要素の何割が見えたら発火するかを決めます。

<motion.div drag="x" dragConstraints={{ left: -100, right: 100 }} />

<motion.section
  initial={{ opacity: 0 }}
  whileInView={{ opacity: 1 }}
  viewport={{ once: true, amount: 0.3 }}
/>

スクロール量に比例して動かしたい場合は、useScroll で進捗のモーションバリューを受け取り、useTransform で別の値に変換します。モーションバリューはReactの再レンダリングを経由せずに値を更新するため、スクロールのように毎フレーム変わる入力に向いています。

アクセシビリティとバンドルサイズの注意点

reducedMotionの初期値neverとuserの指定

Motionは、利用者がOSで「視差効果を減らす」(macOS・iOS)のような動きを減らす設定を選んでいても、初期状態ではそれを尊重しません。MotionConfigContext の初期値が reducedMotion: "never" だからです。公式ドキュメントのアクセシビリティのページは、アプリ全体を MotionConfig で包み、reducedMotion="user" を指定する方法を案内しています。

import { MotionConfig, useReducedMotion, motion } from "motion/react"

export function App({ children }: { children: React.ReactNode }) {
  return <MotionConfig reducedMotion="user">{children}</MotionConfig>
}

// 個別に出し分けたい場合
export function Hero() {
  const reduce = useReducedMotion()
  return <motion.div animate={{ x: reduce ? 0 : 100, opacity: 1 }} />
}

"user" にすると、OSの動きを減らす設定が有効な利用者に対して、変形とレイアウトのアニメーションが無効になり、opacityや背景色のアニメーションは残ります。自動再生の動画や視差スクロールのように、変形以外で動いている要素は useReducedMotion の戻り値で個別に止めます。

LazyMotionとmによる初期ロードの削減

公式の「Reduce bundle size」ページの掲載値によると、motion コンポーネントは宣言的なprops APIを持つため、tree shakingしても34KBより小さくなりません。m コンポーネントと LazyMotion に置き換え、機能を後から非同期で読み込む構成にすると、初回描画に必要な分は4.6KB弱になります。読み込む機能の目安は、domAnimation(アニメーション・variants・exit・tap/hover/focus)で+15KB、domMax(ドラッグとレイアウトアニメーションを追加)で+25KBです。

features={domAnimation} のように同期でimportすると、機能分も初期バンドルに入ります。初回描画を軽くしたいときは、機能を別ファイルに切り出し、動的importで渡します。

// features.ts
import { domAnimation } from "motion/react"
export default domAnimation

// App.tsx
import { LazyMotion } from "motion/react"
import * as m from "motion/react-m"

const loadFeatures = () => import("./features").then((res) => res.default)

export function LazyApp() {
  return (
    <LazyMotion features={loadFeatures} strict>
      <m.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>LazyMotionの表示例</m.div>
    </LazyMotion>
  )
}

strict を付けると、LazyMotion の内側で通常の motion を使ったときにエラーになります。そのため、置き換え漏れで34KBが戻ってくる事態を防げます。

CSSアニメーションや他ライブラリとの使い分け

Motionが強いのは、Reactの状態と連動するアニメーションです。マウントとアンマウント(AnimatePresence)、レイアウト変化の補間、ドラッグ、途中で目標が変わっても速度を引き継ぐspringなどが該当します。逆に、ホバー時の色の変化や、読み込み中の点滅のように状態を持たない装飾だけなら、CSSの transition と @keyframes で足ります。そうした用途のためだけに motion を読み込み、34KBを増やすのは割に合いません。

用途 第一候補
ホバー・フォーカスの色や影 CSS transition
表示・非表示、リストの並べ替え Motion(AnimatePresence・layout)
ページ遷移 View Transitions API
hooksだけでspringを扱う react-spring
タイムライン中心の演出・非React GSAP・Anime.js

ページ遷移は、ブラウザ標準の機能でも実装できます(「View Transitions APIの対応ブラウザと実装手順|クロスドキュメント遷移まで」)。React 19.3以降であれば、Motion 13.4で追加された AnimateView が、ReactのViewTransitionの上に組まれています。hooks中心の設計が好みなら「react-springの使い方|useSpring・useTrail・useChainをv10系で実装する」、タイムラインで演出を組むなら「Anime.js v4の変更点と使い方|v3からの移行手順とGSAPとの違い」と比べてください。

よくある質問

Framer MotionとMotionは別のライブラリですか?

同じライブラリです。2024年11月12日にFramer社から独立し、名称がMotionに変わりました。Reactでは motion/react から読み込み、APIは framer-motion と共通です。

Framer Motion(Motion)は無料で商用利用できますか?

できます。motion と framer-motion はどちらもMITライセンスです。有料のMotion+は追加のコンポーネントや例を提供する別商品で、本体の利用には必要ありません。

framer motionのeasingには何を指定できますか?

linear・easeIn・easeOut・easeInOut・circIn・circOut・circInOut・backIn・backOut・backInOut・anticipate の11種類の名前と、[0.22, 1, 0.36, 1] のような3次ベジェの配列、steps()、自作の関数です。CSSの "ease" は名前として使えません。

framer motionでdelayを設定するには?

transition={{ delay: 0.3 }} のように秒で指定します。delayだけならx・yのspringなどの既定値は保たれます。子要素を順番に遅らせるときは、親のvariantsに delayChildren: stagger(0.08) を書きます。

Next.jsのApp Routerで使えますか?

使えます。ファイルの先頭に "use client" を書いて motion/react から読み込むか、Server Componentのまま import * as motion from "motion/react-client" と書きます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.09.30 テックブログ OpenAI Dotsとは?常時稼働エージェントの権限設計と自社システム接続【2026年9月】
  2. 2026.03.10 コラム 年収の壁【2026年最新】178万円・136万円・130万円の一覧と手取りの分岐点
  3. 2026.04.20 テックブログ Chrome(Gemini)のSkillsとは?使い方・作成手順・利用条件と表示されない時の対処
  4. 2026.09.27 コラム 法定調書合計表とは?令和8年分の書き方と提出義務、給与・支払データからの集計自動化
  5. 2026.09.28 テックブログ タイムズカーの不正アクセスと免許証画像160万件の流出|退会者まで残さない保管設計

RELATED POSTS 関連記事

目次