framer-motion、react-motion、react-native-animatable、react-native-reanimated、react-spring、react-transition-groupは、ReactおよびReact Nativeアプリケーションにアニメーションを導入するための代表的なライブラリです。これらはそれぞれ異なるアプローチでアニメーションを実現しており、パフォーマンス特性、API設計、対応プラットフォーム、開発体験に大きな違いがあります。framer-motionとreact-springは宣言的かつ高性能なWebアニメーションに特化し、react-native-reanimatedはReact Native向けにネイティブスレッドで動作する高度なアニメーションを提供します。一方、react-transition-groupはCSSトランジションや簡単な状態遷移に適しており、react-motionは物理ベースのアニメーションを可能にしますが、現在は非推奨となっています。react-native-animatableはシンプルなプリセットアニメーションを提供しますが、柔軟性に制限があります。
アニメーションはユーザー体験を大きく左右しますが、適切なツール選びは難しいものです。framer-motion、react-spring、react-native-reanimatedなど、それぞれが異なる哲学と技術的アプローチを持っています。この記事では、実際の開発現場で直面する課題を中心に、各ライブラリの使いどころを具体的なコード例とともに解説します。
まず重要な前提として、react-motionは公式に非推奨(deprecated)とされています。GitHubリポジトリおよびnpmページにその旨が明記されており、新規プロジェクトでの使用は避けるべきです。代わりにreact-springが後継として推奨されています。
モーダルや通知、リストアイテムの追加・削除など、コンポーネントの表示・非表示に伴うシンプルなトランジションが必要な場合。
react-transition-group はこの用途に特化しています。CSSトランジションを活用し、クラス名の付与タイミングを制御することで、軽量かつ直感的に実装できます。
// react-transition-group
import { CSSTransition } from 'react-transition-group';
function Modal({ isOpen }) {
return (
<CSSTransition in={isOpen} timeout={300} classNames="fade">
<div className="modal">コンテンツ</div>
</CSSTransition>
);
}
/* CSS */
.fade-enter { opacity: 0; }
.fade-enter-active { opacity: 1; transition: opacity 300ms; }
.fade-exit { opacity: 1; }
.fade-exit-active { opacity: 0; transition: opacity 300ms; }
framer-motion も同様のことが可能ですが、より宣言的です。
// framer-motion
import { AnimatePresence, motion } from 'framer-motion';
function Modal({ isOpen }) {
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
コンテンツ
</motion.div>
)}
</AnimatePresence>
);
}
react-spring では以下のように書けます。
// react-spring
import { useTransition, animated } from '@react-spring/web';
function Modal({ isOpen }) {
const transitions = useTransition(isOpen, {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 },
config: { duration: 300 }
});
return transitions((style, item) =>
item ? <animated.div style={style}>コンテンツ</animated.div> : null
);
}
💡 選択のポイント: 単純なCSSトランジションで十分なら
react-transition-groupが軽量。より複雑な動きや統一されたAPIを望むならframer-motionかreact-spring。
React Nativeでは、JavaScriptスレッドとUIスレッドの分離により、アニメーションの実装方法がWebとは異なります。
react-native-animatable は最もシンプルで、事前定義されたアニメーションを即座に適用できます。
// react-native-animatable
import Animatable from 'react-native-animatable';
<Animatable.View animation="fadeInUp" duration={500}>
<Text>こんにちは</Text>
</Animatable.View>
一方、react-native-reanimated はネイティブスレッドでアニメーションを実行するため、滑らかなパフォーマンスを実現します。スクロール連動アニメーションなど複雑なケースに必須です。
// react-native-reanimated
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
function FadeIn() {
const opacity = useSharedValue(0);
const style = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
// 初回レンダー後にアニメーション開始
useEffect(() => {
opacity.value = withTiming(1, { duration: 500 });
}, []);
return <Animated.View style={style}><Text>こんにちは</Text></Animated.View>;
}
💡 選択のポイント: 単発のシンプルなアニメーションなら
react-native-animatableで十分。しかし、ジェスチャーやスクロールと連動するインタラクティブなアニメーションが必要なら、必ずreact-native-reanimatedを選ぶべきです。
ドラッグ操作やホバー効果など、ユーザーのインタラクションに反応するアニメーション。
framer-motion はこの分野で圧倒的な使いやすさを提供します。
// framer-motion: ドラッグ可能なカード
<motion.div
drag
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
ドラッグ可能
</motion.div>
react-spring でも実現可能ですが、より多くのボイラープレートが必要です。
// react-spring: ドラッグ可能なカード
import { useDrag } from '@use-gesture/react';
import { useSpring, animated } from '@react-spring/web';
function DraggableCard() {
const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }));
const bind = useDrag(({ offset: [x, y] }) => {
api.start({ x, y });
});
return (
<animated.div
{...bind()}
style={{ x, y, touchAction: 'none' }}
>
ドラッグ可能
</animated.div>
);
}
💡 選択のポイント: ジェスチャー対応のインタラクティブアニメーションを素早く実装したいなら
framer-motionが断然有利。react-springは柔軟性が高い反面、設定が煩雑になります。
要素のサイズや位置が変化した際に、自動で滑らかな遷移を適用する機能。
framer-motion はlayoutプロパティでこれを簡単に実現します。
// framer-motion: レイアウト遷移
{items.map(item => (
<motion.div
key={item.id}
layout // これだけで自動遷移
style={{ width: isSelected ? 200 : 100 }}
>
{item.name}
</motion.div>
))}
react-spring では、要素の寸法を監視し、手動でトランジションをトリガーする必要があります。
// react-spring: レイアウト遷移(簡略化例)
const [width, setWidth] = useState(100);
const props = useSpring({ width });
useEffect(() => {
setWidth(isSelected ? 200 : 100);
}, [isSelected]);
return <animated.div style={{ width: props.width }}>{item.name}</animated.div>;
💡 選択のポイント: 自動レイアウト遷移は
framer-motionの独壇場です。react-springでは同等の機能を実現するには大量の追加コードが必要になります。
同じコードベースでWebとモバイルの両方をサポートしたい場合。
react-spring は@react-spring/webと@react-spring/nativeを提供し、ほぼ同一のAPIで両プラットフォームに対応できます。
// react-spring: Web版
import { animated } from '@react-spring/web';
// react-spring: React Native版
import { animated } from '@react-spring/native';
framer-motion はWeb専用であり、React Nativeでは使えません。逆に**react-native-reanimated** はReact Native専用です。
💡 選択のポイント: クロスプラットフォーム対応が必要なら
react-springが唯一の選択肢。Web専用ならframer-motion、React Native専用ならreact-native-reanimatedが最適です。
| ライブラリ | 最適な用途 | プラットフォーム | 主な特徴 |
|---|---|---|---|
framer-motion | Web向け高品質インタラクティブアニメーション、レイアウト遷移 | Webのみ | 宣言的API、ジェスチャーサポート、自動レイアウト遷移 |
react-spring | 物理ベースの自然なアニメーション、クロスプラットフォーム対応 | Web & React Native | スプリング物理モデル、柔軟なhooks、データ駆動 |
react-native-reanimated | React Native向け高性能インタラクティブアニメーション | React Nativeのみ | ネイティブスレッド実行、Worklet、スクロール連動 |
react-native-animatable | React Native向けシンプルなプリセットアニメーション | React Nativeのみ | 即時適用、低学習コスト、柔軟性に欠ける |
react-transition-group | CSSトランジションによるシンプルな出入りアニメーション | Webのみ | 軽量、CSS中心、複雑なアニメーションには不向き |
react-motion | 非推奨 — 新規プロジェクトで使用しないこと | — | — |
framer-motionを第一候補に。react-native-reanimatedが必須。react-springを選ぶ。react-transition-group(Web)またはreact-native-animatable(React Native)で軽量に実装。react-motionは絶対に使わない — 公式で非推奨とされているため、メンテナンスやセキュリティリスクがあります。アニメーションライブラリの選択は、プロジェクトの要件、チームのスキルセット、パフォーマンス要件を総合的に判断することが重要です。過剰な機能は不要な複雑さを生みますが、必要な機能を犠牲にするとユーザー体験が損なわれます。上記の比較を参考に、あなたのプロジェクトに最適なツールを選んでください。
react-transition-groupは、Reactコンポーネントのマウント・アンマウント時のCSSトランジションや簡単な状態遷移を管理する軽量なライブラリです。アニメーション自体はCSSで定義し、ライブラリはクラス名の付与タイミングを制御します。モーダル表示、リストアイテムの追加・削除など、シンプルな出入りアニメーションに最適です。複雑なインタラクティブアニメーションや物理ベースの動きには不向きです。
framer-motionは、Web向けの高品質で宣言的なアニメーションを必要とするプロジェクトに最適です。Layout Animations(自動レイアウト遷移)、Gestureサポート(ドラッグ、ホバー、タップ)、SVGアニメーションなど、幅広い機能を直感的なAPIで提供します。特にNext.jsやGatsbyなどのフレームワークとの統合が容易で、開発体験が非常に良好です。ただし、React Nativeでは使用できません。
react-native-reanimatedは、React Native向けにネイティブUIスレッドでアニメーションを実行できるライブラリです。スクロール連動アニメーションやジェスチャー駆動の複雑なインタラクションを60fpsで実現したい場合に最適です。Workletという特殊な関数構文を使用してJavaScriptコンテキストから切り離すことで、メインスレッドのブロッキングを回避します。ただし、学習コストが高く、デバッグが難しいというトレードオフがあります。
react-springは、WebおよびReact Nativeの両方をサポートする高性能アニメーションライブラリです。スプリング物理モデルに基づき、自然な動きを実現します。useSpring、useTrail、useTransitionなど、目的別に最適化されたhooksを提供し、宣言的かつ柔軟なアニメーション定義が可能です。特に複雑なデータ駆動アニメーションやリスト遷移に強みがあります。ただし、Framer Motionほどの高レベルな抽象化は提供しません。
react-motionは物理ベースのスプリングアニメーションを実現するライブラリですが、公式ドキュメントおよびnpmページで非推奨(deprecated)と明記されています。新規プロジェクトでの使用は避けてください。代わりにreact-springやframer-motionを検討すべきです。
react-native-animatableは、React Nativeアプリで手早くプリセットアニメーション(フェード、スライド、バウンスなど)を適用したい場合に便利です。JavaScriptスレッド上で動作し、設定が簡単ですが、複雑なカスタムアニメーションや高パフォーマンスが求められる場面には向きません。インタラクティブなアニメーションやジェスチャー連携が必要ないシンプルなユースケース向けです。
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-groupandreact-addons-css-transition-group, use the v1 release. Documentation and code for that release are available on thev1-stablebranch.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.
TypeScript definitions are published via DefinitelyTyped and can be installed via the following command:
npm install @types/react-transition-group
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.