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.
tsxconst code = `const MyComponent = () => {return <div style={{fontSize: 100}}>Hello</div>;};`;
To make this executable, first transpile it with @babel/standalone:
tsximport * as Babel from '@babel/standalone';const transpiled = Babel.transform(code, {presets: ['react', 'typescript'],});
After transpilation, the result is this string:
js'const MyComponent = () => { return React.createElement("div", {style: {fontSize: 100}}, "Hello"); };';
Use JavaScript's Function constructor to turn the string into a real function:
tsxconst createComponent = new Function('React', `${transpiled.code}\nreturn MyComponent;`);
This creates a function equivalent to:
tsxfunction 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.
tsxconst 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:
tsximport {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:
tsx// 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:
tsx// 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 componentconst 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.tsxtsximport {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.tstsximport * 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 bodyconstcodeWithoutImports =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/TypeScriptconsttranspiled =Babel .transform (wrappedSource , {presets : ['react', 'typescript'],filename : 'dynamic.tsx',});if (!transpiled .code ) {return {Component : null,error : 'Transpilation failed'};}// Create component with injected APIsconstcreateComponent = 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 Outputtsconst 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 (<AbsoluteFillstyle={{backgroundColor: '#0f0f0f',justifyContent: 'center',alignItems: 'center',}}><divstyle={{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