react-transition-group vs framer-motion vs react-native-reanimated vs react-spring vs lottie-react-native vs react-native-animatable
ReactおよびReact Nativeにおけるアニメーションライブラリの技術的比較
react-transition-groupframer-motionreact-native-reanimatedreact-springlottie-react-nativereact-native-animatable類似パッケージ:
ReactおよびReact Nativeにおけるアニメーションライブラリの技術的比較

framer-motionlottie-react-nativereact-native-animatablereact-native-reanimatedreact-springreact-transition-groupはすべて、ReactやReact Nativeアプリケーションにアニメーションを導入するためのライブラリですが、それぞれ設計思想と適用領域が異なります。

framer-motionは主にWeb向けで、宣言的なAPIと物理ベースのアニメーションにより高品質なインタラクションを実現します。lottie-react-nativeはLottie JSONファイルをReact Nativeで再生するための専用ライブラリです。react-native-animatableはシンプルなプリセットアニメーションを提供し、導入が容易ですが柔軟性に欠けます。react-native-reanimatedはネイティブスレッド上でアニメーションを実行でき、パフォーマンス重視の複雑なジェスチャー対応UIに適しています。react-springは物理法則に基づくアニメーションエンジンで、WebとReact Nativeの両方をサポートします。react-transition-groupはCSSトランジションやアニメーションをReactコンポーネントのライフサイクルに連動させるための低レベルなユーティリティです。

npmのダウンロードトレンド
3 年
GitHub Starsランキング
統計詳細
パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
react-transition-group21,809,24510,256244 kB244-BSD-3-Clause
framer-motion13,748,03530,4872.34 MB3198日前MIT
react-native-reanimated2,493,55610,4993.89 MB3145日前MIT
react-spring925,93428,9938.36 kB1343ヶ月前MIT
lottie-react-native815,52217,084289 kB133ヶ月前Apache-2.0
react-native-animatable621,5759,94659.8 kB1702年前MIT

ReactおよびReact Nativeにおけるアニメーションライブラリの技術的比較

アニメーションは現代のUIにおいて必須の要素ですが、Reactエコシステムには目的や能力が大きく異なる複数のライブラリがあります。この記事では、framer-motionlottie-react-nativereact-native-animatablereact-native-reanimatedreact-springreact-transition-groupの6つを、実際の開発シナリオに基づいて深く比較します。

🎯 アニメーションの種類と実行場所

Web vs React Native

まず重要なのは、ターゲットプラットフォームです。

  • Web専用: framer-motion, react-spring (Web版), react-transition-group
  • React Native専用: lottie-react-native, react-native-animatable, react-native-reanimated
  • クロスプラットフォーム: react-spring(WebとReact Nativeの両方をサポート)

⚠️ 注意: framer-motionはReact Nativeでは動作しません。同様に、lottie-react-nativeはWebでは使えません(Web向けにはlottie-webまたはlottie-reactを使用)。

アニメーションの実行場所

パフォーマンスに大きく影響するのが「どこでアニメーションが実行されるか」です。

  • JavaScriptスレッド: react-native-animatable, react-spring(一部)
    • メインスレッドで実行されるため、重い処理があるとカクつく可能性あり
  • ネイティブスレッド: react-native-reanimated
    • UIスレッドとは分離され、60fpsを安定して維持可能
  • CSS / ネイティブレンダラー: framer-motion, react-transition-group, lottie-react-native
    • ハードウェアアクセラレーションを利用し、スムーズな描画を実現

🧪 基本的なフェードインアニメーションの実装比較

最もシンプルなケース — コンポーネントの表示時にフェードイン — で各ライブラリの使い方を見てみましょう。

framer-motion(Web)

import { motion } from 'framer-motion';

function FadeIn() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.5 }}
    >
      Hello World
    </motion.div>
  );
}

lottie-react-native(React Native)

Lottieは事前作成されたJSONアセットを再生するため、フェードインのような単純なアニメーションには不向きですが、一応表示は可能です。

import LottieView from 'lottie-react-native';

function LottieFade() {
  return (
    <LottieView
      source={require('./fade-in.json')}
      autoPlay
      loop={false}
    />
  );
}

react-native-animatable(React Native)

import Animatable from 'react-native-animatable';

function FadeIn() {
  return (
    <Animatable.View animation="fadeIn" duration={500}>
      <Text>Hello World</Text>
    </Animatable.View>
  );
}

react-native-reanimated(React Native)

import { View } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';

function FadeIn() {
  const opacity = useSharedValue(0);

  // 初期表示時にアニメーション開始
  useEffect(() => {
    opacity.value = withTiming(1, { duration: 500 });
  }, []);

  const animatedStyle = useAnimatedStyle(() => ({
    opacity: opacity.value,
  }));

  return (
    <Animated.View style={[{ padding: 20 }, animated-style]}>
      <Text>Hello World</Text>
    </Animated.View>
  );
}

react-spring(WebまたはReact Native)

// Webの場合
import { animated, useSpring } from '@react-spring/web';

function FadeIn() {
  const props = useSpring({ opacity: 1, from: { opacity: 0 }, config: { duration: 500 } });
  return <animated.div style={props}>Hello World</animated.div>;
}

// React Nativeの場合
import { AnimatedView, useSpring } from '@react-spring/native';

function FadeIn() {
  const props = useSpring({ opacity: 1, from: { opacity: 0 }, config: { duration: 500 } });
  return <AnimatedView style={props}><Text>Hello World</Text></AnimatedView>;
}

react-transition-group(Web)

import { CSSTransition } from 'react-transition-group';
import './Fade.css'; // CSSでopacityのtransitionを定義

function FadeIn({ in: inProp }) {
  return (
    <CSSTransition in={inProp} timeout={500} classNames="fade">
      <div>Hello World</div>
    </CSSTransition>
  );
}

/* Fade.css */
.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  opacity: 1;
  transition: opacity 500ms;
}

🌀 複雑なインタラクション:ドラッグ&スナップ

次に、より高度なユースケース — ドラッグ可能なカードを左右にスワイプしてスナップする — を比較します。

framer-motion(Web)

import { motion, useMotionValue, useTransform } from 'framer-motion';

function DraggableCard() {
  const x = useMotionValue(0);
  const rotate = useTransform(x, [-100, 100], [-10, 10]);

  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: -100, right: 100 }}
      style={{ x, rotate }}
      dragSnapToOrigin
    >
      Swipe me
    </motion.div>
  );
}

react-native-reanimated(React Native)

import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedGestureHandler,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

function DraggableCard() {
  const translateX = useSharedValue(0);

  const gestureHandler = useAnimatedGestureHandler({
    onStart: (_, ctx) => {
      ctx.startX = translateX.value;
    },
    onActive: (event, ctx) => {
      translateX.value = ctx.startX + event.translationX;
    },
    onEnd: () => {
      if (Math.abs(translateX.value) > 50) {
        translateX.value = withSpring(translateX.value > 0 ? 100 : -100);
      } else {
        translateX.value = withSpring(0);
      }
    },
  });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }));

  return (
    <PanGestureHandler onGestureEvent={gestureHandler}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Text>Swipe me</Text>
      </Animated.View>
    </PanGestureHandler>
  );
}

react-spring(Web)

import { useDrag } from '@use-gesture/react';
import { animated, useSpring } from '@react-spring/web';

function DraggableCard() {
  const [{ x, rotate }, api] = useSpring(() => ({ x: 0, rotate: 0 }));

  const bind = useDrag(({ offset: [ox], last }) => {
    if (last) {
      if (Math.abs(ox) > 50) {
        api.start({ x: ox > 0 ? 100 : -100, rotate: ox > 0 ? 10 : -10 });
      } else {
        api.start({ x: 0, rotate: 0 });
      }
    } else {
      api.start({ x: ox, rotate: ox / 10 });
    }
  });

  return <animated.div {...bind()} style={{ x, rotate }}>Swipe me</animated.div>;
}

💡 注: lottie-react-nativereact-native-animatablereact-transition-groupはこのようなインタラクティブなドラッグ操作には対応していません。

📱 パフォーマンスと制限

react-native-animatableの現状

公式GitHubリポジトリ(https://github.com/oblador/react-native-animatable)によると、このライブラリは現在**メンテナンスモード**に入っており、新機能の追加は行われていません。単純なアニメーションには引き続き使えますが、新しいプロジェクトでは`react-native-reanimated`や`react-spring`を検討すべきです。

react-native-reanimatedの制約

  • ネイティブスレッドで実行されるため、JavaScriptのロジック(例:console.log、カスタム関数)をアニメーション中に直接実行できません。
  • デバッグが難しく、エラーメッセージが分かりにくいことがあります。
  • ジェスチャー処理にはreact-native-gesture-handlerとの併用が必須です。

framer-motionの強み

  • layoutプロパティを使うと、要素のサイズや位置が変更された際に自動でスムーズなトランジションを適用できます。
  • whileHoverwhileTapなどのインタラクションハンドラが組み込みで提供されています。
<motion.button
  whileHover={{ scale: 1.1 }}
  whileTap={{ scale: 0.95 }}
>
  Click me
</motion.button>

🔁 状態駆動型アニメーションの比較

UI状態(例:開閉、選択)に応じてアニメーションを切り替えるケースです。

react-spring

const [{ height }, api] = useSpring(() => ({ height: 0 }));

const toggle = () => {
  api.start({ height: isOpen ? 0 : 200 });
};

return (
  <animated.div style={{ height, overflow: 'hidden' }}>
    Content
  </animated.div>
);

framer-motion

<motion.div
  animate={{ height: isOpen ? 200 : 0 }}
  transition={{ duration: 0.3 }}
  style={{ overflow: 'hidden' }}
>
  Content
</motion.div>

react-transition-group

<CSSTransition in={isOpen} timeout={300} classNames="slide">
  <div>Content</div>
</CSSTransition>

/* CSSでheightのtransitionを定義 */

🧩 適切なライブラリの選び方:まとめ

ユースケース推奨ライブラリ
Web向け高品質インタラクション(ページ遷移、ドラッグ、スクロール連動)framer-motion
React NativeでLottie JSONを再生lottie-react-native
React Nativeで簡単なフェード・スライドアニメーション(小規模プロジェクト)react-native-animatable(※新規プロジェクトでは避けるべき)
React Nativeで高性能なジェスチャー対応UI(例:カスタムナビゲーション)react-native-reanimated
物理ベースの自然なアニメーションをWebとReact Nativeで共通化react-spring
CSSトランジションをReactのマウント/アンマウントに連動react-transition-group

💡 最終的なアドバイス

  • Web開発者: framer-motionが多くのケースで最適です。ただし、非常にシンプルなトランジションならreact-transition-groupで十分な場合もあります。
  • React Native開発者:
    • Lottieを使うなら → lottie-react-native
    • 複雑でパフォーマンスが重要なUIなら → react-native-reanimated
    • クロスプラットフォームで物理アニメーションを使いたいなら → react-spring
    • 単純なアニメーションで急いでいるなら → react-native-animatable(ただし長期的には移行を検討)

どのライブラリを選ぶにせよ、アニメーションは「目的」ではなく「手段」であることを忘れないでください。ユーザー体験を向上させるために、適切なツールを選びましょう。

選び方: react-transition-group vs framer-motion vs react-native-reanimated vs react-spring vs lottie-react-native vs react-native-animatable
  • react-transition-group:

    react-transition-groupは、CSSトランジションやキーフレームアニメーションをReactのマウント・アンマウントタイミングに連動させたい場合に使います。例えば、モーダルの表示・非表示やリストアイテムの追加・削除時の滑らかな演出に適しています。独自のアニメーションエンジンは持たず、既存のCSSと連携する点が特徴です。

  • framer-motion:

    framer-motionは、Web向けの高度なインタラクション(ページ遷移、ドラッグ操作、スクロール連動など)を実装したい場合に最適です。特にデザインシステムとの統合や、モーションの微調整が必要なプロダクトで力を発揮します。ただし、React Nativeでは使用できません。

  • react-native-reanimated:

    react-native-reanimatedは、ネイティブスレッドでアニメーションを実行できるため、60fpsを維持する必要がある複雑なUI(例:カスタムナビゲーション、スワイプ可能なリスト、高度なジェスチャー)に最適です。ただし、学習コストが高く、デバッグも難しいため、単純なアニメーションにはオーバーキルです。

  • react-spring:

    react-springは、物理ベースの自然な動き(バネ、減衰など)をWebとReact Nativeの両方で実現したい場合に有効です。アニメーションの状態管理が直感的で、パフォーマンスも良好ですが、Lottieのような事前作成済みアセットの再生には使えません。

  • lottie-react-native:

    lottie-react-nativeは、デザイナーがAdobe After Effectsで作成したLottie JSONアニメーションをReact Nativeアプリ内で忠実に再生したい場合に選択します。ベクター形式で軽量かつスケーラブルですが、インタラクティブな制御(例:ユーザー入力に応じた変化)には向いていません。

  • react-native-animatable:

    react-native-animatableは、すぐに使えるプリセットアニメーション(フェードイン、スライドなど)を手軽に導入したい場合に向いています。小規模なプロジェクトやプロトタイピングに便利ですが、カスタムアニメーションや高性能なジェスチャー対応UIには不向きです。

react-transition-group のREADME

react-transition-group npm

ATTENTION! To address many issues that have come up over the years, the API in v2 and above is not backwards compatible with the original React addon (v1-stable).

For a drop-in replacement for react-addons-transition-group and react-addons-css-transition-group, use the v1 release. Documentation and code for that release are available on the v1-stable branch.

We are no longer updating the v1 codebase, please upgrade to the latest version when possible

A set of components for managing component states (including mounting and unmounting) over time, specifically designed with animation in mind.

Documentation

TypeScript

TypeScript definitions are published via DefinitelyTyped and can be installed via the following command:

npm install @types/react-transition-group

Examples

Clone the repo first:

git@github.com:reactjs/react-transition-group.git

Then run npm install (or yarn), and finally npm run storybook to start a storybook instance that you can navigate to in your browser to see the examples.