RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-24中級

Rork アプリで Reanimated の worklet エラーが出たときに順番に疑う 6 つのポイント

Rork 生成コードに Reanimated を組み込んだ途端に出る worklet 関連のエラーを、babel プラグインの抜け漏れから closure キャプチャまで、現場でぶつかる順に切り分けていきます。

Rork515Reanimated10workletトラブルシューティング77React Native209アニメーション10

Rork で作ったアプリに気の利いたアニメーションを入れたくて React Native Reanimated を追加した瞬間、赤い画面いっぱいに Reanimated 2 failed to create a worklet だの Tried to synchronously call function from a different thread だのが出てきて手が止まる、という経験はありませんか。私自身、Rork Companion のプレビューでは軽やかに動いていたトランジションが、EAS でビルドした実機でだけ worklet エラーを吐き、原因を詰めるのに半日かかりました。

Reanimated の worklet は書き味こそ通常の関数とほとんど変わりませんが、裏側では UI スレッド上で独立して実行されるよう Babel プラグインが変換をかけています。そのため「同じ書き方なのにエラーが出る/出ない」が起こりがちです。ここでは Rork 生成コードでよくぶつかる順に、6 つのチェックポイントを上から潰していく流れでまとめます。Reanimated v3 系を前提にしていますが、v2 系でもほぼ同じ考え方で切り分けられます。

まず「どの段階で落ちているか」を見分ける

worklet まわりのエラーは、大きく 3 つのレイヤーに分かれます。

  • ビルド時に Babel プラグインが変換できていない(開発ビルド起動時点で落ちる)
  • 初回レンダリング時に worklet が生成できない(useAnimatedStyle などの呼び出しで落ちる)
  • 実行時にスレッド越境で落ちる(ジェスチャーやアニメーションが走った瞬間に止まる)

最初に確認したいのは、Metro のコンソールに出ているメッセージがどのレイヤーのものかです。Reanimated 2 failed to create a worklet, maybe you forgot to add Reanimated's babel plugin? が出ていればレイヤー 1、ReferenceError: foo is not defined のようなものが worklet 内から出ていればレイヤー 2、Tried to synchronously call function 系であればレイヤー 3 です。ログを見ずに手を動かすと、同じ現象の別原因を踏み続けて時間を溶かします。

1. babel.config.js に react-native-reanimated/plugin が入っているか

最頻出の原因はこれです。Rork が自動生成した babel.config.js には Expo のプリセットしか入っていない場合があり、Reanimated を手動で npm install した直後に発生します。

// ❌ プラグイン未設定のまま
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
  };
};
// ✅ 必ず plugins 配列の最後に置く
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: ["react-native-reanimated/plugin"],
  };
};

ポイントは 配列の最後に置くことです。公式ドキュメントにも明記されていますが、他のプラグインより後ろにないと worklet 化の変換が正しく走りません。変更後は npx expo start --clear のように Metro のキャッシュをクリアしないと反映されないので、ここまでセットで覚えておくのがおすすめです。

もう 1 点ありがちなのが、Metro のプロセスが裏でしぶとく残っているケースです。babel.config.js を直して --clear で起動し直したのに同じ worklet エラーが出続けるときは、古い Metro インスタンスが動き続けている可能性があります。killall -9 node で一度すべての Node プロセスを落としてから起動し直すと、ほとんどの場合すんなり通ります。派手ですが、再発防止として覚えておいて損はありません。

2. ネイティブ側が古いまま残っていないか

Reanimated はネイティブモジュールを同梱しているため、JavaScript 側を更新しただけでは動きません。Rork Companion で確認できていた挙動が EAS ビルドや実機インストール済みアプリで再現しないときは、ほぼこのパターンです。

# 開発用ビルドをクリーンにやり直す
npx expo prebuild --clean
npx expo run:ios    # または run:android
 
# EAS 側でキャッシュが残っているときは
eas build --platform ios --clear-cache

prebuild --cleanios/ android/ ディレクトリを作り直すので、中身を手で編集している場合は差分を退避してから実行してください。Rork が生成した初期状態のまま使っているなら、そのまま実行して問題ありません。

3. worklet 内で普通の JS 関数を呼び出していないか

worklet の中で呼べるのは worklet だけ、というのが Reanimated のいちばん重要なルールです。Rork が生成したコードでは、通常のユーティリティ関数を useAnimatedStyle の中で素直に呼び出しているケースが結構あります。

// ❌ worklet の中から JS スレッドの関数を呼ぶとエラー
import { useAnimatedStyle } from "react-native-reanimated";
 
function clamp(value: number, min: number, max: number) {
  return Math.min(Math.max(value, min), max);
}
 
const style = useAnimatedStyle(() => {
  // 実行時に「Tried to synchronously call function clamp」などが出る
  const opacity = clamp(progress.value, 0, 1);
  return { opacity };
});
// ✅ 関数側に "worklet"; を付けて worklet 化する
function clamp(value: number, min: number, max: number) {
  "worklet";
  return Math.min(Math.max(value, min), max);
}
 
const style = useAnimatedStyle(() => {
  const opacity = clamp(progress.value, 0, 1);
  return { opacity };
});

関数の 1 行目に "worklet"; ディレクティブを置くと、Babel プラグインがその関数も worklet として変換してくれます。共通ユーティリティは utils/worklets.ts のようにまとめ、すべての関数に "worklet"; を宣言しておくと、使うたびに迷わなくて済みます。

4. JS スレッドの状態を更新したいのに runOnJS を忘れていないか

worklet 側から React の setState やナビゲーションを呼びたくなる場面は多いですが、直接呼ぶと 3 番と同じエラーになります。この場合は runOnJS でラップして JS スレッドに戻す必要があります。

// ❌ worklet の中で setState を直接呼んでいる
import { useAnimatedGestureHandler } from "react-native-reanimated";
 
const gestureHandler = useAnimatedGestureHandler({
  onEnd: () => {
    setSwipeCount(prev => prev + 1); // JS スレッドの関数を直接呼んでいる
  },
});
// ✅ runOnJS で JS スレッドに明示的に戻す
import { useAnimatedGestureHandler, runOnJS } from "react-native-reanimated";
 
const gestureHandler = useAnimatedGestureHandler({
  onEnd: () => {
    runOnJS(setSwipeCount)(swipeCount + 1);
  },
});

runOnJS は値しか渡せないので、setSwipeCount(prev => prev + 1) のような関数型更新を使いたい場合は、JS 側で受けるラッパー関数を用意しておくときれいに書けます。

5. worklet が最新の state を見ていない(closure 問題)

worklet は定義された時点の値をキャプチャしてしまいます。React コンポーネントの state や props を直接参照していると、古い値を掴み続けて「動いてはいるけれど最新の値にならない」という紛らわしい状態になります。

// ❌ 普通の state を worklet から参照すると古い値のままになる
const [threshold, setThreshold] = useState(100);
 
const style = useAnimatedStyle(() => {
  return { opacity: progress.value > threshold ? 1 : 0 };
});

対処は 2 つあります。ひとつは、shared value に移してしまう方法です。

// ✅ useSharedValue に移す
const threshold = useSharedValue(100);
 
// 変更するとき
threshold.value = 150;
 
const style = useAnimatedStyle(() => {
  return { opacity: progress.value > threshold.value ? 1 : 0 };
});

もうひとつは、useDerivedValue で JS 側の値を監視する方法です。外部ライブラリの state などを使いたいときはこちらが便利です。

// ✅ useDerivedValue で JS 側の値をブリッジ
const thresholdShared = useDerivedValue(() => {
  return threshold; // JS 値の変更を検知して worklet 側に伝える
}, [threshold]);

依存配列を忘れると更新されないので、useEffect と同じ感覚で揃えておくのが安全です。

6. Hermes と Expo Go のバージョン不整合

見落としがちですが、Expo Go の古いバージョンでアプリを確認すると、ネイティブ側の Reanimated バージョンが噛み合わずに worklet が動かないことがあります。Rork Companion と実機で挙動が違うときは、app.jsonruntimeVersion と使っている Expo Go のバージョンを合わせているかを確認してください。

# Reanimated のバージョン確認
npx expo install --check
# 期待: react-native-reanimated が Expo SDK の推奨バージョンに揃っている

Expo の案内するバージョンからずれていると、新しい API が内部で未実装だったり、worklet の変換が合わなかったりします。npx expo install react-native-reanimated で入れ直すと、SDK に対応したバージョンに寄せてくれます。実機で確認するときは、開発ビルド(development build)を作って Expo Go 依存から抜けておくと、この種のトラブルは一気に減ります。

実用上の小技として、学習用の最小再現画面をプロジェクト内に 1 枚だけ残しておくのもおすすめです。スプリングと PanGestureHandler を組み合わせただけの簡単な画面で、worklet の各パターンを切り離して試せる「サンドボックス」として使えます。本番画面でエラーが出たときは、再現コードをそのサンドボックスに持ち込んで切り出すと、他の処理に引っ張られずに本質だけを確認できます。ごちゃごちゃした画面でデバッグしていると、半日溶かす原因になりがちです。

また、Rork 公式の Companion と EAS だけで開発している方は、npx expo install react-native-reanimated を自分で叩かない限りバージョン不整合にぶつかることはほぼありません。Expo SDK を自分でアップグレードしたときは、直後に npx expo install --check を必ず回す──これを習慣にしておくと、バージョン不整合系のエラーはほぼシャットアウトできます。

次にやること

worklet 関連のエラーは「書き方の問題」に見えて、実際はビルド設定・スレッドモデル・キャッシュの問題であることがほとんどです。今日すぐにやるなら、babel.config.js を開いて react-native-reanimated/plugin が最後に入っているかを確認し、開発ビルドを --clear つきで作り直すだけでも半数以上のケースが片付きます。

より複雑なジェスチャー連携まで踏み込む場合は、関連記事の Rork で Reanimated とジェスチャーハンドラを組み合わせる高度なアニメーション設計 と Rork アプリのクラッシュ・白い画面をデバッグする完全ガイド を先に読んでおくと、今回のチェックリストが線でつながって理解が深まるはずです。

worklet の「書けるけど何が起きているか分からない」状態から抜け出したい方には、手元に置いておいて損のない一冊です。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-05-06
Rorkアプリのアニメーションがカクつく — フレームドロップの根本原因を特定して直した手順
Rorkで生成されたアニメーションがカクつく問題の根本原因と修正手順を解説します。JSスレッドとUIスレッドの分離、useNativeDriverの使い方、フレームドロップの診断方法まで実践的にまとめました。
開発ツール2026-03-30
Rork × React Native Reanimated & Gesture Handler 実践ガイド — 60fps を維持するアニメーション設計と高度なジェスチャーインタラクション
Reanimated 3 と Gesture Handler で 60fps を保つ設計を、壁紙・癒し系アプリの実運用で踏んだ落とし穴とともに整理しました。runOnJS の発火遅延、useDerivedValue キャッシュ、FlashList との併用、cancelAnimation 漏れなど、公式ドキュメント外の知見を実測値付きで共有します。
開発ツール2026-07-08
Reanimatedで作る、滑るインジケーター付きセグメントコントロール
標準のセグメントコントロールがAndroidで浮いて見える問題を、Reanimatedの自作コンポーネントで解決します。幅を実測して指標を滑らせる完全なコードと、UIスレッド駆動・アクセシビリティ・RTL対応までを実装メモとしてまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →