Just-in-time compilation of Remotion code
If you have generated a Remotion component as a string (for example using LLMs), you can compile it in the browser to display a live preview.
const code = `
const MyComponent = () => {
return <div style={{fontSize: 100}}>Hello</div>;
};
`;To make this executable, first transpile it with @babel/standalone:
import * as Babel from '@babel/standalone';
const transpiled = Babel.transform(code, {
presets: ['react', 'typescript'],
});After transpilation, the result is this string:
'const MyComponent = () => { return React.createElement("div", {style: {fontSize: 100}}, "Hello"); };';Use JavaScript's Function constructor to turn the string into a real function:
const createComponent = new Function('React', `${transpiled.code}\nreturn MyComponent;`);This creates a function equivalent to:
function createComponent(React) {
const MyComponent = () => { return React.createElement("div", ...); };
return MyComponent;
}Calling this function and passing the actual React library makes the component available.
The component code is wrapped inside, and dependencies (in this case just React) are passed in as arguments.
const Component = createComponent(React);Adding Remotion APIs
Just React won't be enough for animations.
Remotion components need access to APIs like useCurrentFrame() and <AbsoluteFill>. These are injected the same way.
All APIs used within the code must be explicitly passed in.
Here's an example with a few Remotion APIs:
import {AbsoluteFill, useCurrentFrame, spring} from 'remotion';
const createComponent = new Function('React', 'AbsoluteFill', 'useCurrentFrame', 'spring', `${transpiled.code}\nreturn MyComponent;`);
const Component = createComponent(React, AbsoluteFill, useCurrentFrame, spring);Each parameter name becomes an available variable inside the executed code.
Handling Import Statements
AI-generated code typically includes import statements.
Since APIs are injected manually via the Function constructor, these imports need to be stripped:
// AI generates this:
import {useCurrentFrame, AbsoluteFill} from 'remotion';
export const MyComposition = () => {
const frame = useCurrentFrame();
return <AbsoluteFill>...</AbsoluteFill>;
};The imports are removed and just the component body is extracted:
// Step 1: Remove imports (these are injected manually)
const codeWithoutImports = code.replace(/^import\s+.*$/gm, '').trim();
// Step 2: Extract component body from "export const X = () => { ... };"
const match = codeWithoutImports.match(/export\s+const\s+\w+\s*=\s*\(\s*\)\s*=>\s*\{([\s\S]*)\};?\s*$/);
const componentBody = match ? match[1].trim() : codeWithoutImports;
// Step 3: Wrap it back into a component
const wrappedSource = `const DynamicComponent = () => {\n${componentBody}\n};`;Complete Example
In this example, a React component is compiled and rendered by the <Player> component.
- Preview.tsx
- use-compilation.ts
- Input code
import {Player } from '@remotion/player';
import {useCompilation } from './use-compilation';
export const Preview : React .FC <{code : string}> = ({code }) => {
const {Component , error } = useCompilation (code );
if (error ) {
return <div style ={{color : 'red'}}>Error: {error }</div >;
}
if (!Component ) {
return null;
}
return <Player component ={Component } durationInFrames ={150} fps ={30} compositionWidth ={1920} compositionHeight ={1080} controls />;
};import * as Babel from '@babel/standalone';
import React , {useMemo } from 'react';
import {AbsoluteFill , useCurrentFrame , useVideoConfig , spring , interpolate , Sequence } from 'remotion';
interface CompilationResult {
Component : React .ComponentType | null;
error : string | null;
}
export function useCompilation (code : string): CompilationResult {
return useMemo (() => {
if (!code ?.trim ()) {
return {Component : null, error : null};
}
try {
// Strip imports and extract component body
const codeWithoutImports = code .replace (/^import\s+.*$/gm, '').trim ();
const match = codeWithoutImports .match (/export\s+const\s+\w+\s*=\s*\(\s*\)\s*=>\s*\{([\s\S]*)\};?\s*$/);
const componentBody = match ? match [1].trim () : codeWithoutImports ;
const wrappedSource = `const DynamicComponent = () => {\n${componentBody }\n};`;
// Transpile JSX/TypeScript
const transpiled = Babel .transform (wrappedSource , {
presets : ['react', 'typescript'],
filename : 'dynamic.tsx',
});
if (!transpiled .code ) {
return {Component : null, error : 'Transpilation failed'};
}
// Create component with injected APIs
const createComponent = new Function ('React', 'AbsoluteFill', 'useCurrentFrame', 'useVideoConfig', 'spring', 'interpolate', 'Sequence', `${transpiled .code }\nreturn DynamicComponent;`);
const Component = createComponent (React , AbsoluteFill , useCurrentFrame , useVideoConfig , spring , interpolate , Sequence );
return {Component , error : null};
} catch (error ) {
return {
Component : null,
error : error instanceof Error ? error .message : 'Unknown compilation error',
};
}
}, [code ]);
}const generatedCode = `import {useCurrentFrame, AbsoluteFill, spring, useVideoConfig} from 'remotion';
export const MyComposition = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const scale = spring({
frame,
fps,
config: {damping: 10, stiffness: 100},
});
return (
<AbsoluteFill
style={{
backgroundColor: '#0f0f0f',
justifyContent: 'center',
alignItems: 'center',
}}
>
<div
style={{
fontSize: 120,
color: 'white',
transform: \`scale(\${scale})\`,
}}
>
Hello World
</div>
</AbsoluteFill>
);
};`;Security
Note that this code runs in the global scope of the browser.
It can access all global variables and functions.
For production applications, consider running the code in a sandboxed <iframe>, and setting a content security policy.
This is outside the scope of this guide.
See Also
- Prompt to Motion Graphics template - Example implementation
- Code Generation - How to generate Remotion code with AI
- System Prompt - Teaching LLMs about Remotion