更新日:2026年5月27日
React Native Reanimated 4は、CSS Animations / Transitions APIを公式採用した最初のメジャーバージョンで、宣言的な記法だけでUIスレッド上のアニメーションを書けるようになった2025年9月リリースのライブラリです。Worklet(useSharedValue / useAnimatedStyle)はそのまま残りつつ、シンプルな状態遷移はCSSプロパティだけで完結します。正直に言うと、最初に触ったときは「これJSXじゃなくてCSS書いてるのか?」と二度見しました。本記事では、Reanimated 4の新機能、Reanimated 3からの移行手順、New Architecture(Fabric)との互換性、そして本番アプリで実測したパフォーマンスの注意点を、私が実プロジェクトでハマったポイントも交えてまとめます。
Reanimated 4の主な新機能
Reanimated 4は、Software Mansionが2025年9月に正式リリースしたメジャーバージョンで、最大の目玉はCSS Animations と CSS Transitions の公式サポートです。これまで「JS側の宣言から、Worklet経由でUIスレッドへ値を流す」必要があった処理を、styleプロップにCSSと同じプロパティ名を渡すだけで実現できます。私が直近のリリースで触った範囲では、ローディングインジケータやモーダルのフェード、リスト行のエンタリングなど、いわゆる「定型」のアニメーションがすべてCSS表記に置き換わりました。コードレビューに出した時の差分の小ささだけで価値があります。
その他、本番で効いてくる変更点は次の通りです:
- workletsパッケージの分離:従来
react-native-reanimated/pluginに含まれていたWorklet変換がreact-native-workletsに切り出されました。Babel設定の更新が必要です。 - Legacy(Paper)レンダラーの非対応:Reanimated 4はFabric専用です。Old Architectureから移行できないアプリは、Reanimated 3.x系のメンテナンスブランチを利用することになります。
- Layout Animationsの安定化:
entering/exiting/layoutプロップが安定APIとして昇格し、FadeInなどのプリセットがツリーシェイク可能になりました。 - 新しい型推論:
useAnimatedStyleの戻り値がViewStyle系の型を厳格に推論するようになり、誤ったプロパティをUIスレッドへ渡す事故が減りました。 - React Compiler対応の改善:Worklet本体に対するReact Compilerのフォースキップ指示が公式に組み込まれ、安全に共存できます。
公式リリースノートはReanimatedドキュメントから辿れます。バージョン履歴はGitHubリリースページで必ず確認してください。
Reanimated 3からReanimated 4への移行方法は?
移行の難易度はアプリの規模よりも「New Architectureがすでに有効か」で決まります。先にFabricを有効化していれば、移行作業の8割は依存とBabel設定の更新だけで終わります。逆に、まだPaperレンダラーで動作させているなら、Reanimated 4の前にNew Architecture移行を完了させる必要があります。Old ArchitectureでReanimated 4を試そうとして、起動直後にjsi::JSErrorでクラッシュするのは典型的なハマりポイントです(私も最初これでハマって、ログを30分眺めました)。ExpoユーザーはExpo SDK 55アップグレードガイドを先に通すと、SDKレベルで前提条件が揃います。
具体的な手順は次の通りです。
- React Native 0.76以上、もしくはExpo SDK 54以上に上げる。
newArchEnabled=true(Android)とRCT_NEW_ARCH_ENABLED=1(iOS)を確認する。- 依存を更新:
npm install react-native-reanimated@^4 react-native-worklets@^0.6。 babel.config.jsを更新(後述)。useSharedValueなどのインポートはそのままでOK。型エラーが出た箇所はTypeScript 5.5以上で再ビルドして確認。- iOSは
pod install、Androidは./gradlew cleanを実行。
// babel.config.js — Reanimated 4
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
// 旧: 'react-native-reanimated/plugin'
'react-native-worklets/plugin',
],
};
};CSS Animations APIの使い方
Reanimated 4のCSS Animations APIは、Webと同じ@keyframes相当の構造をJavaScriptオブジェクトで宣言し、それをスタイルのanimationNameに渡す形を取ります。useSharedValueもuseAnimatedStyleも不要で、純粋にスタイルだけで完結します。私が新規プロジェクトで採用したときの感想は「ロジックとアニメーションが切り離せるので、デザイナーが直接差分を提案できる」。実際、PRレビューでアニメーション挙動のすり合わせにかかる時間が体感で半分以下になりました。
import { View, Text } from 'react-native';
import type { CSSAnimationKeyframes } from 'react-native-reanimated';
const pulse: CSSAnimationKeyframes = {
'0%': { opacity: 1, transform: [{ scale: 1 }] },
'50%': { opacity: 0.6, transform: [{ scale: 1.08 }] },
'100%': { opacity: 1, transform: [{ scale: 1 }] },
};
export function PulsingBadge() {
return (
<View
style={{
width: 64,
height: 64,
borderRadius: 32,
backgroundColor: '#2563eb',
animationName: pulse,
animationDuration: '1200ms',
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
}}
>
<Text style={{ color: '#fff', textAlign: 'center' }}>LIVE</Text>
</View>
);
}サポートされるCSSプロパティはanimationDelay・animationDirection・animationFillMode・animationPlayStateなど、Web仕様の主要セットをほぼカバーしています。一点だけ注意:animationNameには文字列ではなく、上記のようにキーフレームオブジェクトを渡します。これはWebの@keyframesがCSSセレクタ前提なのに対し、React Nativeのスタイルがオブジェクト前提のためです。
CSS Transitionsで状態遷移を宣言する
キーフレーム不要の「Aの状態からBの状態へ」というシンプルな遷移には、CSS TransitionsのほうがCSS Animationsより向いています。transitionPropertyに対象プロパティを、transitionDurationに時間を、transitionTimingFunctionにイージングを指定するだけで、該当プロパティの値が変わった瞬間に自動でトゥイーンされます。実装としてはuseStateで書ける範囲のロジックに、UIスレッドで滑らかな動きを足せるのが強みです。
import { useState } from 'react';
import { Pressable, View } from 'react-native';
export function ExpandableCard() {
const [open, setOpen] = useState(false);
return (
<Pressable onPress={() => setOpen(v => !v)}>
<View
style={{
height: open ? 220 : 80,
backgroundColor: open ? '#0ea5e9' : '#1e293b',
padding: 16,
borderRadius: 12,
transitionProperty: ['height', 'backgroundColor'],
transitionDuration: '220ms',
transitionTimingFunction: 'ease-out',
}}
/>
</Pressable>
);
}同じことをuseSharedValue + withTimingで書くと、共有値の宣言、useAnimatedStyle、useEffectでの更新トリガーが必要になります。私の現場では、状態遷移系のコードがざっくり3分の1の行数になりました。複雑なジェスチャー連動が絡まない限り、CSS Transitions側を第一選択にしています。
既存のWorklet API(useSharedValue / useAnimatedStyle)との使い分け
CSS APIが登場しても、WorkletベースのuseSharedValue / useAnimatedStyle / useDerivedValueは廃止されません。むしろReanimated 4ではWorkletランタイムが独立パッケージ化されたことで内部実装が綺麗になり、安定度が増しています。CSS APIは「宣言的に書ける」範囲では強力ですが、フレーム単位の補間結果に応じて他の値も派生させたい、あるいはGesture HandlerのonUpdateからリアルタイムに値を渡したい、といったケースではWorkletが必要です。
判断軸を整理すると次の通りです:
- 固定のキーフレーム/自動再生(ローディング、点滅、無限ループ)→ CSS Animations
- 状態切替に追従する単純なトゥイーン(開閉、色変更、表示・非表示)→ CSS Transitions
- ジェスチャー連動(ドラッグ、ピンチ、スワイプ)→
useSharedValue+ Gesture Handler - スクロール位置に応じた連続変形(パララックス、ヘッダー縮小)→
useAnimatedScrollHandler - 物理ベースの動き(バネ、減衰)→
withSpringとuseSharedValue
Reanimated 4はNew Architectureに対応していますか?
はい。むしろReanimated 4はNew Architecture(Fabric + TurboModules)専用です。Old ArchitectureのPaperレンダラーでは起動しません。これは「対応している」というより「前提条件」で、私はこの方針自体には強く賛成です。Fabricの同期レンダリングがあって初めて、UIスレッド側でスタイルの値を直接書き換えるCSS APIの仕様が成立するためです。逆に、まだ社内アプリでPaper前提のネイティブモジュールが残っている場合は、その移行が先になります。
確認手順は次の通りです:
android/gradle.propertiesにnewArchEnabled=trueがあるか確認。- iOSのPodfileで
ENV['RCT_NEW_ARCH_ENABLED'] = '1'または:fabric_enabled => trueが有効か確認。 - Expoの場合は
app.jsonの"newArchEnabled": trueを確認(SDK 54でデフォルト、SDK 55で必須)。 - 起動時のログに
Running "App" with {"fabric":true}のように出ていれば正しく有効。
Fabric自体の仕組みはReact Native公式アーキテクチャドキュメントに詳しいので、移行前に一度目を通しておくことをおすすめします。
パフォーマンスとUIスレッドの実測ポイント
Reanimated 4のパフォーマンスを語るうえで重要なのは「UIスレッド上で完結するかどうか」です。CSS APIもWorklet APIも、最終的な値の補間はUIスレッド側で走るため、JSスレッドが詰まっていても60fps(ハイエンド端末では120fps)を維持できます。違いはオーバーヘッドの形で、私が中堅機(Pixel 6a)で計測した範囲では次のような傾向でした。
| 項目 | CSS Animations / Transitions | Worklet API(useSharedValue) |
|---|---|---|
| 初回マウント時のJS負荷 | 低(フックなし) | 中(フック分のメモリ確保) |
| UIスレッド上での補間コスト | 同等 | 同等 |
| 同時アニメーション100個 | 劣化なし | 劣化なし |
| ジェスチャー連動 | 非対応 | 最適 |
| 動的なキーフレーム差し替え | 限定的 | 柔軟 |
| コード量 | 少ない | 多い |
| React Compiler相性 | そのまま安全 | Worklet除外指示が必要 |
体感差が出たのは「リスト行のentering / exiting」でした。同じ200行のスクロールリストで、FadeInプリセットをWorkletで適用したケースと、animationNameでCSS化したケースを比較すると、後者のほうがJSヒープの増加が小さく、Profilerでもフック関連の処理が消えるぶん明確に軽くなります。リスト最適化のより広い議論はReact Nativeリスト完全ガイドでも触れています。
Reanimated 4でよくある落とし穴と対策
1. Babelプラグインの置き忘れ
もっとも多いのはreact-native-reanimated/pluginをreact-native-worklets/pluginに置き換え忘れて、Worklet変換が走らないケースです。_WORKLET is not definedやReading 'value' of undefinedといったエラーが出たら、まずBabel設定を疑ってください。
2. animationNameに文字列を渡してしまう
Webの感覚でanimationName: 'pulse'と書くと無視されます。必ずキーフレームのオブジェクトを渡してください。プロジェクト内で再利用するキーフレームは、ファイル上部にconstで定義しておくと型推論も効きます。
3. useEffectでCSS Transitionsを発火させようとする
CSS Transitionsは「スタイル値の変化」に反応するので、useEffectからのトリガーは不要です。むしろ二度発火して、初回マウント時に不要なアニメーションが走ることがあります。Reactの通常の状態更新でそのまま遷移します。
4. New Architecture未有効のままインストール
iOSのみFabricを有効、Androidは未有効、という片肺状態でCIが通ってしまうことがあります。両プラットフォームでFabricフラグが立っていることを必ず確認してください。私の知る限り、これはCIだけ通ってQAで落ちる事故の鉄板パターンです。
よくある質問
Reanimated 4はExpo Goで動きますか?
はい、Expo SDK 54以降のExpo Goに同梱されています。ただしSDK 53以前のExpo GoはOld Architecture前提のため動作しません。development buildではNew Architectureが有効になっていれば問題なく動きます。
CSS AnimationsとWorklet、どちらを学べばよいですか?
新規にReact Nativeアニメーションを学ぶ場合はCSS APIから入って、ジェスチャーや物理挙動が必要になった時点でWorkletに進むのが最短ルートです。CSS APIの方が概念が少なく、Web経験との接続も滑らかです。
Reanimated 4でレイアウトアニメーションは安定しましたか?
はい、entering / exiting / layoutプロップが安定APIになりました。FadeIn・SlideInRightなどのプリセットもツリーシェイク可能で、未使用プリセットがバンドルに含まれなくなりました。
React Compilerと併用すると問題は起きませんか?
Reanimated 4はWorklet本体に対するReact Compilerのスキップ指示を内部に持っているため、明示的な'use no memo'を追加しなくても安全に併用できます。CSS APIに至っては純粋なスタイルなので、そもそもReact Compilerの最適化対象外です。
Old Architectureのままで使う方法はありますか?
ありません。Reanimated 4はFabric専用です。Old Architectureを維持する必要がある場合は、Reanimated 3.x系のメンテナンスリリースを使い、New Architecture移行のタイミングでReanimated 4に上げる計画にしてください。


