We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide14 min read

Remotion, or a video file as the output of code

Remotion generates video files from React components. How the timeline works, rendering, a licence tied to company size, and where it genuinely fits.

Remotion, or a video file as the output of code

Remotion lets you write video in React and render it to a file. Components, styles, animations, and logic are exactly what they are in an ordinary application, except the output is not a page but a recording.

The idea sounds eccentric until you see the use case. Generating a thousand personalised films, each holding the recipient's name and their data, is impossible in a classic editing program, and here it is a loop over a list.

Before going further, one caveat, since without it the whole text would mislead: this is not an open source project in the usual sense. The licence depends on your company's size, and I cover it below.

The licence, the thing to check first

The code is public and can be read, while the right to commercial use depends on how many people your organisation employs.

Free use covers individuals and companies employing at most three people. Above that threshold a company licence is required, and it carries two billing tracks, which is exactly where the figures get confused.

VariantRateWho it is for
Per render0.01 USD per render, 100 USD monthly minimumApplications generating video automatically, where a developer needs no seat
Per seat25 USD per seat per monthMaking videos by hand, one seat per person
Extended licenceFrom 500 USD per monthCustom terms, prioritised support, additional products

The vendor attaches the hundred dollar monthly minimum to the render billed track, so on seats alone the bill starts at twenty five dollars per person.

That threshold surprises teams more often than you might expect. A four person company using the tool on a client project is already on the paid side, regardless of how many developers actually touch the video code.

Know too what version five is preparing, since it concerns what the tool sends outward while working. The current line is still 4.0, and version five has not shipped yet. Among the announced changes, usage reporting through a licence key becomes mandatory for every company licence holder regardless of billing track, while people on the free licence pass a value marking free use in the same place.

Practical advice: check the terms on the vendor's site before starting a project rather than after finishing it. A licence of this class is no obstacle, while it can be an unpleasant surprise discovered at invoicing time.

How it works

The basic concept is a composition: a description of the video covering size, frames per second, and length expressed in frames.

Code
TypeScript
import { Composition } from 'remotion'

export const RemotionRoot = () => (
  <Composition
    id="Greeting"
    component={Greeting}
    durationInFrames={150}
    fps={30}
    width={1920}
    height={1080}
    defaultProps={{ name: 'Anna' }}
  />
)

A hundred and fifty frames at thirty frames per second gives five seconds. That arithmetic is present everywhere here and worth getting used to, since animations are described in frames rather than seconds.

The component itself looks like an ordinary React component, with one difference: it knows which frame it is currently rendering.

Code
TypeScript
import { useCurrentFrame, interpolate } from 'remotion'

export const Greeting = ({ name }: { name: string }) => {
  const frame = useCurrentFrame()

  const opacity = interpolate(frame, [0, 30], [0, 1], {
    extrapolateRight: 'clamp',
  })

  return (
    <div style={{ opacity, fontSize: 80 }}>
      Hello, {name}
    </div>
  )
}

That is the whole idea. Animation is not a separate mechanism but a function of the frame number, so every frame renders independently and deterministically. The same frame rendered twice yields an identical result.

The consequence is practical and pleasant: rendering can be parallelised, since frames do not depend on each other. You can also inspect any moment of the recording without playing everything from the start.

Rendering and deployment

The preview runs in a browser and resembles an ordinary development server, with a timeline added for scrubbing.

Code
Bash
npx remotion studio
npx remotion render Greeting output.mp4 --props='{"name":"Anna"}'

Rendering launches a browser without an interface, captures successive frames, and assembles them into a file. That means it demands considerable resources: processor, memory, and disk space for intermediate frames.

From that follows the main deployment decision. Rendering on your application server blocks it for the duration, so a more sensible arrangement is a separate job queue or serverless functions invoked on demand, each rendering a portion of the recording.

Keep execution time limits in mind, since with serverless functions they are hard. Rendering split into portions of a dozen or so seconds of material fits typical limits, while a portion covering a whole recording usually does not. Deploying with a provider such as Vercel, check the maximum function duration before designing the split, since that determines the portion size rather than the other way round.

Measure rendering time early, since intuition misleads. A minute of high resolution video is eighteen hundred frames, each requiring a page render and an image capture. With complex animations a minute of material can take several minutes to render, so a promise of instant film generation needs checking against your own case.

Note too that media processing moved to a new engine. The audio and video tags recommended for new projects now come from a separate package, while their counterparts in the main package were renamed to browser based variants and remain as a fallback. When upgrading between major versions it pays to read the change list, since it concerns exactly those package and component names.

What it genuinely serves

Separating the uses where the tool wins from those where it loses to an editing program pays off.

It wins at bulk generation from data. A thousand year in review films for a thousand users, each with different numbers and charts. A hundred product clips generated from a catalogue on every price change. Automatic recordings holding results, reports, or statistics.

It wins at repeatable formats. A video with a quote over a background, a clip with a podcast excerpt and subtitles, an animated card with a news headline. Anywhere the layout is fixed and only the content changes.

It also wins at video tied to an application. A preview generated from what the user configured, a shareable card holding their result, an animation built from their data.

It loses on a single film of arbitrary form. A camera recording, an interview, an advertisement with shots and music will be cut faster in an ordinary program, since there you work on a timeline rather than on frame numbers.

It also loses when a non technical person is meant to work on the material. Changing a caption's colour here requires editing code and running a render, which for an editor is a step backwards.

Audio, video, and synchronisation

A recording without sound rarely suffices, and that is an area where working in frames requires changing habits.

You add an audio track as a component, giving its start moment in frames. The library synchronises it with the picture during rendering, so there is none of the drift that occurs with manual assembly.

Code
TypeScript
import { Sequence, staticFile, useVideoConfig } from 'remotion'
import { Audio } from '@remotion/media'

export function Recording() {
  const { fps } = useVideoConfig()

  return (
    <>
      <Audio src={staticFile('voiceover.mp3')} />
      <Sequence from={2 * fps} durationInFrames={5 * fps}>
        <Caption text="Order received" />
      </Sequence>
    </>
  )
}

Computing moments from the frame rate rather than typing numbers directly is a habit worth forming from the first project. Changing the composition from thirty to sixty frames then shifts everything correctly, while hardcoded values drift out of sync with the audio.

Embedding existing video footage works similarly, with one limitation worth knowing. The browser preview plays material in real time while rendering pulls individual frames from it, so the two modes can differ slightly on footage with a different frame rate from the composition. Matching the frame rate removes that problem at source.

Subtitles synchronised with speech are among the most common uses and deserve a methodical approach. A transcript with timestamps converts into a list of segments with frame numbers, and a component displays whichever falls on the current frame. The result is more precise than manual placement in an editing program and repeatable on every text change.

Note that audio lengthens rendering less than picture does while complicating parallel deployment. Portions rendered separately must be assembled together with the audio track, so the merging step must know about both.

Working with data

Since generation from data is the biggest advantage, how to pass that data into a recording deserves describing.

The simplest route is composition props supplied at render time. That suffices for simple cases and stops sufficing once the data is large or comes from a database.

A more sensible arrangement then fetches the data before rendering and passes a finished set.

Code
TypeScript
const data = await db.yearSummary(userId)

await renderMedia({
  composition,
  serveUrl,
  codec: 'h264',
  outputLocation: `out/${userId}.mp4`,
  inputProps: data
})

Rendering should be deterministic, so fetching data while frames are produced is risky: two runs can yield different results, and under parallel rendering each portion would query the source separately. It pays to describe the shape of the passed data with a schema, since an error in it surfaces only on the finished recording.

Load images and fonts through the mechanisms the library provides rather than through ordinary tags.

Code
TypeScript
import { Img, staticFile, delayRender, continueRender } from 'remotion'

const [handle] = useState(() => delayRender('loading font'))

useEffect(() => {
  document.fonts.load('700 48px Inter').then(() => continueRender(handle))
}, [handle])

<Img src={staticFile('logo.png')} />

Rendering captures a frame once the page is ready, so a resource loading a fraction of a second too late simply will not appear in shot. The pair of calls delaying and resuming the render states plainly that the frame is not ready yet, and that is the right answer wherever something loads asynchronously. That is the most common cause of recordings whose first frames come out empty.

For bulk generation, plan failure handling too. Rendering a thousand films always ends with a few errors, and the process should record which items failed rather than aborting everything on the five hundredth.

Code
TypeScript
const failed: Array<{ id: string; reason: string }> = []

for (const user of users) {
  try {
    await render(user)
  } catch (error) {
    failed.push({ id: user.id, reason: String(error) })
  }
}

await fs.writeFile('failed.json', JSON.stringify(failed, null, 2))
console.log(`done: ${users.length - failed.length}, errors: ${failed.length}`)

Writing the failure list to a file rather than to a log carries practical weight here: a retry comes down to reading that file and walking only it, instead of searching a log for which of a thousand items need repeating.

Remotion against the alternatives

OptionStrengthWeaknessPick it when
RemotionVideo from data, a familiar language, repeatabilityLicence tied to company size, rendering costBulk generation from data
An editing programFull freedom, visual workNo automation, manual workA single film of arbitrary form
MotionBrowser animation, lightweightProduces no video fileAnimation on a page, not a recording
Command line video toolsVery fast, no browserAwkward with complex layoutsSimple cutting, joining, converting

The third row gets confused with the first, and they are entirely different things. Browser animation is interactive and lives on a page, while a video is a file you can send, post to a social service, or play without an internet connection.

The fourth row deserves considering before you reach for the first. If the task amounts to adding a caption to an existing recording or joining two files, a command line tool does it in seconds, without launching a browser and without a licence.

How to start sensibly

The order that saves the most time on a first project differs from what the documentation suggests.

Start with one, the simplest format, and carry it all the way through, including rendering and delivering the file wherever it is meant to go. Only that full pass shows where the real problems sit, and they usually concern neither animation nor design but render time and the place the process has to run.

Do not start with a complicated animation. Working in frames demands different thinking from working on a timeline, so the first project is better spent absorbing that difference on a simple layout.

Build a preview for non technical people as early as possible. A studio running locally suffices for a developer, while the person commissioning the material needs to see the result without installing anything. Generating a few variants and sending them as files solves that more simply than building an interface.

Measure cost and time at realistic scale before promising anything. Ten videos rendered locally say little about a thousand rendered in parallel, since that adds limits, queueing, and failure handling.

Finally, check the licence and count the cost of developer seats if the company exceeds the free threshold. That is a business decision better taken at the start than after building the whole process.

Common mistakes

The first is starting a project without checking the licence. The three person company threshold catches teams regularly, and discovering it at invoicing time is awkward.

The second is rendering on the server handling traffic. The work occupies processor and memory for minutes, so it belongs in a separate job queue or in functions invoked on demand.

The third is describing animation in seconds rather than frames. The whole library operates on frame numbers, and mixing units produces animations that drift when the frame rate changes.

The fourth is promising instant generation without measuring. A minute of material can take several minutes to render, so check the time against a real case before promising anything.

The fifth is using it for single films of arbitrary form. An editing program does that faster and without writing code.

The sixth is handing the tool to a non technical person. Every change requires editing code and rendering, so without a developer the process stalls.

FAQ

Is Remotion free?

For individuals and companies employing at most three people, yes, including commercial use. Above that threshold a company licence is required: 25 USD per seat per month for hand made work, or 0.01 USD per render for automated generation, where a hundred dollar monthly minimum applies. Check the terms on the vendor's site before starting a project.

Is it an open source project?

The code is public and can be read, while the licence is not permissive: the right to commercial use depends on organisation size. That distinction deserves understanding, since a public repository gets confused with a licence allowing any use.

How long does rendering take?

It depends on complexity and resolution. A minute of high resolution material is eighteen hundred frames, each requiring a page render, so with complex animations the time can exceed the recording's length several times over. Measure it against your own case before promising deadlines.

Will it replace an editing program?

Not for a single film of arbitrary form, since visual work is faster there. It will replace one for bulk generation from data and for repeatable formats, where the layout is fixed and only the content changes.

Where should rendering run in production?

Away from the server handling user traffic. Sensible arrangements are a separate job queue on a dedicated machine, or serverless functions rendering portions in parallel, which on longer recordings cuts waiting time several fold.

Documentation sits on the project site, and the licence terms on a separate page.