Skip to main content

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.

tsx
const code = `
const MyComponent = () => {
return <div style={{fontSize: 100}}>Hello</div>;
};
`;

To make this executable, first transpile it with @babel/standalone:

tsx
import * 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:

tsx
const createComponent = new Function('React', `${transpiled.code}\nreturn MyComponent;`);

This creates a function equivalent to:

tsx
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.

tsx
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:

tsx
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:

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 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
tsx
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 />;
};

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