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
Preview.tsximport {Player } from '@remotion/player'; import {useCompilation } from './use-compilation'; export constPreview :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 />; };
use-compilation.tsimport * asBabel from '@babel/standalone'; importReact , {useMemo } from 'react'; import {AbsoluteFill ,useCurrentFrame ,useVideoConfig ,spring ,interpolate ,Sequence } from 'remotion'; interfaceCompilationResult {Component :React .ComponentType | null;error : string | null; } export functionuseCompilation (code : string):CompilationResult { returnuseMemo (() => { if (!code ?.trim ()) { return {Component : null,error : null}; } try { // Strip imports and extract component body constcodeWithoutImports =code .replace (/^import\s+.*$/gm, '').trim (); constmatch =codeWithoutImports .match (/export\s+const\s+\w+\s*=\s*\(\s*\)\s*=>\s*\{([\s\S]*)\};?\s*$/); constcomponentBody =match ?match [1].trim () :codeWithoutImports ; constwrappedSource = `const DynamicComponent = () => {\n${componentBody }\n};`; // Transpile JSX/TypeScript consttranspiled =Babel .transform (wrappedSource , {presets : ['react', 'typescript'],filename : 'dynamic.tsx', }); if (!transpiled .code ) { return {Component : null,error : 'Transpilation failed'}; } // Create component with injected APIs constcreateComponent = newFunction ('React', 'AbsoluteFill', 'useCurrentFrame', 'useVideoConfig', 'spring', 'interpolate', 'Sequence', `${transpiled .code }\nreturn DynamicComponent;`); constComponent =createComponent (React ,AbsoluteFill ,useCurrentFrame ,useVideoConfig ,spring ,interpolate ,Sequence ); return {Component ,error : null}; } catch (error ) { return {Component : null,error :error instanceofError ?error .message : 'Unknown compilation error', }; } }, [code ]); }
Example AI Outputconst 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