Remotion開発者ワークフロー:デバッグ・生産性向上・よくあるエラーの解決策
Remotion開発者ワークフロー:デバッグ・生産性向上・よくあるエラーの解決策
Remotionは開発者体験の良いツールです。しかし、通常のReactアプリとは異なるレンダリングモデルを持つため、そのモデルを深く理解しているかどうかで「なんとなく動かせる」と「効率よく動かせる」の間に大きな差が生まれます。
この記事では、Remotionを使った1日の開発フローを体系的に解説します。Remotion Studioでの作業環境の整え方、シングルフレームレンダリングによる素早い確認、ESLintプラグインを使った事前品質チェック、非同期データ読み込みのパターン、レンダリング速度のベンチマークと最適化、そしてよくあるエラーの意味と解決策まで、実装レベルで踏み込んで説明します。
Remotion Studioを主開発環境として使いこなす
npx remotion studioで起動するRemotionStudioは、コンポジション開発の主な作業場所です。ブラウザベースのUIで以下のことができます。
- タイムラインのスクラブ(任意のフレームへの即座のジャンプ)
- ファイル保存時のライブリロード(Vite HMRによる即時反映)
- スキーマ定義済みコンポジションのプロップスエディタ
- ローカルレンダリングの起動
- フレーム単位のナビゲーション用キーボードショートカット
最も効率的なワークフローは、Studioをブラウザタブで常時開いたままエディタと並べて作業することです。コンポーネントファイルの変更は保存と同時にほぼリアルタイムで反映されます。アニメーションの見た目の確認はフルレンダリングを毎回行う必要はありません。Studioでスクラブするほうがはるかに速く、全フレームの状態を把握できます。
Studioのキーボードショートカット:
Space— 再生/一時停止←/→— 1フレーム移動Shift + ←/→— 10フレーム移動0— フレーム0へジャンプ- フレーム番号を直接入力してEnter — 任意フレームへ移動
フレーム単位の移動に慣れることが重要です。アニメーションのバグは特定のフレームで発生することが多く、目視でスクラブするより数値指定で飛んだほうが圧倒的に速く問題箇所に到達できます。
シングルフレームレンダリングで素早く確認する
特定のフレームが実際のレンダリング品質でどう見えるかを確認したい場合、Studioのプレビューはウィンドウサイズに合わせてスケールされるため実際の出力と異なることがあります。フレームの正確な見た目を確認するには、CLIから単一フレームをレンダリングします。
# PNG静止画として出力
npx remotion still src/index.ts MyComposition --frame=42 out/frame-42.png
# 1フレームだけのMP4として出力(フルレンダリングと同じコードパスを使う)
npx remotion render src/index.ts MyComposition out/check.mp4 --frames=42-42
--framesフラグはフレーム範囲を指定します。--frames=42-42は42フレーム目だけをレンダリングします。renderStillはPNGスナップショット向けに速く、--frames指定レンダリングはモーションブラーや音声同期の確認に向いています。
このパターンが特に役立つ場面:
- フォント・文字サイズ・改行の状態を実際の解像度で確認するとき
<Img>アセットが正しくロードされているか確認するとき- データ駆動のビジュアル(例:グラフが特定のフレームで正しい値を表示しているか)を検証するとき
Remotionコンポーネントのconsole.logについて
新しいRemotionユーザーが最初に混乱する点のひとつがconsole.logの挙動です。Studioプレビュー中にコンポーネント内のconsole.logの出力が見えない、という経験をした方も多いはずです。
理由はシンプルです:StudioプレビューはブラウザのJavaScriptエンジンでコンポーネントを実行します。console.logの出力はブラウザのDevToolsコンソールに流れます。DevToolsを開いていなければ見えません。
CLIレンダリング中はRemotionがヘッドレスChromiumを起動し、そのコンソール出力を--log=verboseフラグ付きでターミナルに転送します。
npx remotion render src/index.ts MyComposition out/output.mp4 --log=verbose
--log=verboseなしではエラーのみが表示されます。付与すると、コンポーネント内のconsole.logを含む詳細なレンダリング情報が流れます。
実践的なデバッグTips: データ駆動アニメーションをデバッグするときは、コンポーネントのトップにconsole.logを置いてから以下のコマンドを実行します。フルレンダリングなしでフレーム0時点のデータ状態を確認できます。
npx remotion render src/index.ts MyComposition out/debug.mp4 \
--frames=0-0 \
--log=verbose
delayRender/continueRenderで非同期データを扱う
APIやファイルシステムからデータを取得してからレンダリングを開始したい場合、delayRenderとcontinueRenderでRemotionにフレームキャプチャを待機させます。このパターンはRemotionでの非同期処理の標準的なやり方です。
import { delayRender, continueRender } from "remotion";
import { useEffect, useState } from "react";
export const DataComp: React.FC<{ endpoint: string }> = ({ endpoint }) => {
const [data, setData] = useState<ChartData | null>(null);
const [handle] = useState(() => delayRender("チャートデータを読み込み中"));
useEffect(() => {
fetch(endpoint)
.then((res) => res.json())
.then((json) => {
setData(json);
continueRender(handle); // 成功時に呼ぶ
})
.catch((err) => {
console.error("データ読み込み失敗:", err);
continueRender(handle); // エラー時も必ず呼ぶ
});
}, [endpoint]);
if (!data) return null;
return <BarChart data={data} />;
};
厳守すべきルールが3つあります。
1. エラーパスでも必ずcontinueRenderを呼ぶ。呼ばないとレンダリングがタイムアウト(デフォルト30秒)まで無限に待機します。
2. delayRenderを条件付きで呼ばない。useState(() => delayRender(...))の初期化子パターンを使って、コンポーネントマウント時に必ず一度だけ呼ばれるようにします。
3. 複数の非同期リクエストがある場合は複数のdelayRenderハンドルを使う。各delayRenderには対応するcontinueRenderを必ず1:1で用意します。
delayRenderの引数(説明文字列)はRemotionStudioのUIや--log=verboseの出力に表示されます。「Loading chart data」「Fetching user profile」のように具体的に書くと、ハングしたときにどの処理で止まっているか即座に特定できます。
@remotion/eslint-pluginで問題を早期発見する
@remotion/eslint-pluginはRemotionコンポーネント特有の問題を静的に検出するESLintプラグインです。レンダリング時になって初めて発覚するバグのクラスを事前に潰すことができます。
npm install --save-dev @remotion/eslint-plugin
// .eslintrc.json
{
"plugins": ["@remotion"],
"extends": ["plugin:@remotion/recommended"]
}
プラグインが検出する主な問題:
非決定論的関数の使用:Math.random()、Date.now()、new Date()をコンポーネント内で使うとフレームのたびに異なる値が返り、Remotionの「同じフレームは常に同じ見た目」という保証が崩れます。プラグインはこれらを検出し、useCurrentFrame()から値を導出する代替案を提示します。
continueRenderの未対応:delayRenderを呼んでいるがcontinueRenderの対応する呼び出しがない箇所を検出します。
フックの誤用:useCurrentFrame()やuseVideoConfig()がRemotionコンテキスト外で呼ばれている場合を検出します。
このプラグインをCIパイプラインに組み込んでレンダリング前に実行するのが理想的です。レンダリングを途中で失敗させるより、コーディング時点で問題を検出するほうがはるかにコストが低いです。
inputPropsとcalculateMetadataを使った高速イテレーション
inputPropsはレンダリング時にコンポジションへ動的データを注入するRemotionの仕組みです。開発中は、プロップスを変えてすぐに結果を確認するための反復サイクルの主軸になります。
calculateMetadataはさらに進んで、入力プロップスからコンポジションのデュレーションとキャンバスサイズを動的に算出できます。
import { CalculateMetadataFunction } from "remotion";
type Props = {
slides: SlideData[];
framesPerSlide: number;
};
export const calculateMetadata: CalculateMetadataFunction<Props> = async ({
props,
}) => {
return {
durationInFrames: props.slides.length * props.framesPerSlide,
fps: 30,
};
};
<Composition
id="SlideShow"
component={SlideShow}
calculateMetadata={calculateMetadata}
fps={30}
width={1920}
height={1080}
defaultProps={{
slides: sampleSlides,
framesPerSlide: 90,
}}
/>
このパターンにより、コンポジションはデータの量に応じて長さが変わるため、実際のコンテンツで正確な見積もりができます。defaultPropsにリアルなサンプルデータを入れておくと、開発中にStudioで本番に近い状態を確認できます。
レンダリング速度のベンチマークと最適化
レンダリングが想定より遅い場合、原因を特定してから対処します。
ステップ1:ベースラインを計測する
time npx remotion render src/index.ts MyComposition out/output.mp4
ステップ数ではなく実時間で記録します。
ステップ2:delayRenderのオーバーヘッドを確認する
非同期データ読み込みがある場合、各フェッチリクエストがレンダリングを待機させます。本番環境ではライブAPIコールをレンダリング前に一度だけ実行するようcalculateMetadataでデータを先読みし、inputProps経由で渡します。
export const calculateMetadata: CalculateMetadataFunction<Props> = async ({
props,
}) => {
const data = await fetchDataOnce(props.dataEndpoint);
return {
props: { ...props, preloadedData: data },
durationInFrames: data.length * 30,
};
};
ステップ3:フレームごとの計算量を減らす
コンポーネント内のすべての処理は、フレームをレンダリングするたびに実行されます。フレーム間で変わらないデータ(配列のソート、文字列の正規表現処理等)はuseMemoでメモ化します。
const sortedItems = useMemo(() => {
return items.slice().sort((a, b) => b.value - a.value);
}, [items]); // Remotionでは再レンダリング間でitemsは変わらない
ステップ4:並列度を上げる
renderMedia APIのconcurrencyオプションで複数フレームの並列レンダリングを有効化できます。
await renderMedia({
composition,
serveUrl,
codec: "h264",
outputLocation: "out/output.mp4",
concurrency: 4,
});
デフォルトはCPUコア数の半分です。増やすことでCPU律速のレンダリングは速くなりますが、メモリ消費も増加します。
ステップ5:長尺動画はRemotionLambdaへ
10分以上の動画など、ローカルで時間がかかりすぎるケースはRemotionLambdaで分散レンダリングします。200個のLambdaインスタンスに分割すれば、ローカルで8分かかるレンダリングが1分以下になります。
—log=verboseによる詳細デバッグ
レンダリングが想定外に遅い、または途中でハングする場合の第一のツールです。
npx remotion render src/index.ts MyComposition out/output.mp4 --log=verbose
verboseログで確認できる情報:
- フレームカウントの進捗(
Rendering frame X of Y) delayRenderの呼び出しと解決タイミング- コンポーネントからの
console.log出力 - 各ステップのタイミング情報
特定のフレームで止まる場合、そのフレーム番号をメモして--frames=N-N --log=verboseで単独レンダリングします。最初の数フレームだけ異常に時間がかかる場合はdelayRenderの問題(非同期フェッチの待機)である可能性が高いです。
よくあるエラーとその意味
「A delayRender() was called but its matching continueRender() was never called after 30000ms.」
delayRender()を呼んだが対応するcontinueRender()がタイムアウト内に呼ばれなかった。useEffectのエラーパスでcontinueRenderを呼んでいるか確認。コンポーネントが非同期処理完了前にアンマウントされていないか確認。
「Error: The component returned null on frame X but something else on frame Y.」
コンポーネントがデータの読み込み状態によってnullを返すことがある。データが未ロードのときはdelayRenderでレンダリングを待機させ、nullをローディング状態として使わないようにする。
「useCurrentFrame() was called outside of a Remotion context.」
useCurrentFrame()がコンポジションのレンダリングツリー外(ユーティリティ関数や通常のReactコンポーネントなど)で呼ばれた。フックはコンポジションコンポーネントのルートで呼び、値をプロップスとして渡す。
「FFmpeg failed with exit code 1.」
コーデックとコンテナの不一致が多い。h264は.mp4、vp8/vp9は.webm、proresは.movの組み合わせが正しい。また音声コーデックもビデオコーデックと互換性があるか確認する。
プレビューが真っ白になる
コンポーネントのどこかでエラーが起きてReactがキャッチしている。DevToolsのコンソールを開いてエラーメッセージを確認する。典型的な原因は未ロードデータへのプロパティアクセス(Cannot read properties of undefined)や不正なCSS値。
「Cannot find module ‘remotion’」
remotionパッケージがインストールされていない。npm installを実行してから再試行。package.jsonにremotionが依存関係として記載されているか確認。
RenderCompテンプレートで開発環境を整える
Remotionのワークフローを素早く確立するための近道として、RenderCompはプロダクションレディなテンプレートを提供しています。各テンプレートにはinputPropsスキーマ、calculateMetadataパターン、可変長コンポジション、整理されたファイル構成が含まれており、ボイラープレートフェーズをスキップできます。rendercomp.comでユースケース別のテンプレートをご確認ください。
よくある質問
Q1: RemotionStudioはどのブラウザで使うべきですか? Chrome(Chromium系)が推奨です。RemotionのCLIレンダリングはヘッドレスChromiumを使用するため、ChromeでプレビューすることでレンダリングされたMPと最も近い見た目を確認できます。Safari等でも動作しますが、フォントレンダリングや一部CSS挙動が異なる場合があります。
Q2: APIキーなどの環境変数をRemotionレンダリングに渡すには?
コンポーネントやcalculateMetadata内でprocess.env.YOUR_VARを参照します。CLIレンダリングではMY_KEY=xxx npx remotion render ...のように変数をプレフィックスするか、.envファイルをRemotionのVite設定で読み込みます。Node.jsレンダラーAPIを使う場合は、呼び出し元プロセスの環境変数が自動的に継承されます。
Q3: renderMediaとrenderStillの違いは何ですか?
renderMediaは全フレームをレンダリングしてMP4等のビデオ/オーディオファイルを生成します。renderStillは1フレームだけをPNG・JPEG・WebPとして出力します。サムネイル生成、OGP画像生成、デバッグ目的のフレーム確認にはrenderStillが向いています。
Q4: Studioではスムーズに見えるのに、レンダリングしたMP4がカクカクします。なぜですか?
StudioプレビューはリアルタイムでHTMLを描画するため、重いコンポジションではフレームが落ちます。エクスポートされたMP4は常にフレームバイフレームで正確にレンダリングされます。カクカくしているように見える場合は、アニメーション値に急激な変化がないか--framesで各フレームを確認してください。
Q5: Remotionコンポーネント内でReact QueryやSWRを使えますか?
技術的には可能ですが、delayRender/continueRenderパターンの使用を推奨します。React QueryやSWRはブラウザUIアプリのキャッシュ・再検証モデル向けに設計されており、Remotionの決定論的なレンダリングモデルとの相性が良くありません。
Q6: ローカルでは正常にレンダリングされるのに、Remotion Lambdaで失敗します。なぜ?
最も多い原因は(1)環境変数が未設定、(2)ローカルでは動くがLambda環境からアクセスできないアセットURL、(3)Lambdaにインストールされていないフォント、のいずれかです。--log=verboseをLambdaデプロイ先に対して実行し、エラーメッセージを確認してください。
Q7: 大規模なRemotionプロジェクトでバンドルビルドを高速化するには? RemotionはViteをバンドラーに使っています。大きなコンポジションを複数のエントリーポイントに分割すること、重い依存関係に動的インポートを使うこと、Vitキャッシュを不必要にクリアしないことが基本的な高速化手順です。