Skip to content

How to make Videos with React using Remotion

How to make Videos with React using Remotion

We've written a blog post discussing how React truly is a "Learn Once, Write Anywhere" library. In that article, we talked about how we aren't limited to rendering React components to the DOM.

In this post, we'll discuss how we can leverage our knowledge of developing React components and create Videos, using Remotion. If you already know [React] and follow along with this post, you'll walk away with a custom .mp4 video of you're own creation, and the knowledge of how to customize it to you're heart's content.

Remotion

Remotion is a suite of libraries for creating videos programmatically with React.

Just like react-dom is used to provide an interface between React components, and rendering to the DOM, Remotion is used to provide an interface between React and rendering to Video.

If you know React, you can make videos. -- Remotion Documentation

This is exciting because it allows developers to re-use their knowledge of the React library for yet another purpose. Let's dive in and see how we can start rendering some videos!

Initializing a Remotion Video Project

Creating a new Remotion video is as easy as a 1 line command:

npm init video

Running this command gives the user access to a few default templates for Remotion video development:

  • Hello World
  • Blank
  • Hello World (Vanilla Javascript)
  • React Three Fiber
  • Still Images
  • Text to Speech
CLI Templates

The Hello World templates are fantastic places to go if you are a hands-on learner and like playing around with your code in a live dev environment. They're how I learned to use Remotion. They're packed with such great logic, that I'd encourage you to run a template before moving on and exploring them a bit to see how things are laid out.

That being said, I want to go over things rather slowly, so I'm going to create a repo from the Blank template, add a few development features (linting, husky, prettier, etc), and use that as a starting point. If you want to develop with these features and want a similar starting point, clone the repo here and you can see every commit from beginning to end. This is the exact commit after I finished installing linting to the Blank template.

The Blank Remotion Template

If you cloned the repo I mentioned above, you'll be working in the Blank template, with a few additional dev features.

The main 2 files are:

  • src\index.tsx: This is similar to the index file of a regular create-react-app. It uses a render function to take a React component, and render it to the desired output, in this case, a .mp4 video. We won't be looking in here.
  • src\Video.tsx: This is the main entry point of our video. Think of this as the App component in a create-react-app project. It's here that we layer together React components to create our Remotion video. This is where we will start to focus our attention.

The Composition component

The entry point file Video.tsx introduces us to our first Remotion component, Composition.

<Composition
  id="Empty"
  component={EmptyComponent}
  durationInFrames={60}
  fps={30}
  width={1920}
  height={1080}
/>

We can see this component in action by running npm start and checkouting out our development environment:

Empty Composition

The Remotion development environment has 4 important sections:

  • Compositions: The left main column. This is a list of all defined Composition components in Video.tsx. The Blank template defines a single Composition, with an id == 'Empty'
  • Scenes: The bottom left column. We haven't talked about scenes yet...
  • Composition Render: The main dev zone. This is the development render of the currently selected Composition.
  • Composition Timeline: This is a timeline of scenes rendered in the currently selected Composition.

The Composition component has a few important properties:

  • id: This is the name given to the Composition, used to identify the component in the development environment.
  • component: This is the component that the Composition renders. Different compositions can render the same component, for responsiveness for example.
  • durationInFrames & fps: A Composition is not defined by how long it is in time, but by how many frames it is made up of, and how fast those frames are rendered.
  • width & height: Together, these define the aspect ratio of our video. You could define an HD Composition with a 1920/1080 width/height, or a square still from a single frame of a Component with a 200/200 width/height for example.
  • defaultProps: This is the props object passed into the component when the Composition is rendered.

This repository defines a super small empty component as the component-to-render for the Empty composition. Let's rename our component to HelloWorld, and create a full-fledged component to render. We'll render a simple, 4-second video, of the text "Hello, World!", in high definition, at 60fps.

src/Video.tsx

import { HelloWorld } from './components/HelloWorld';

<Composition
  id="HelloWorld"
  component={HelloWorld}
  durationInFrames={240}
  fps={60}
  width={1920}
  height={1080}
  defaultProps={{
    text: 'Hello, World!',
  }}
/>

src/components/HelloWorld.tsx

import { FC } from 'react';

type HelloWorldProps = {
 text: string;
};
export const HelloWorld: FC<HelloWorldProps> = ({ text }) => (
 <div
  style={{
   display: 'flex',
   justifyContent: 'center',
   alignItems: 'center',
   flex: 1,
   backgroundColor: 'black',
   color: 'cyan',
   fontSize: 200,
   fontFamily: 'Arial, Helvetica, sans-serif',
  }}
 >
  <div>{text}</div>
 </div>
);

To render this to .mp4 you need to have ffmpeg installed. Adjust the package.json build command and instruct the CLI to render our HelloWorld composition, and run npm run build to create our first video!

Here's a link to what this would render. It's nothing crazy, but with previous React knowledge, and with the help of Remotion, we have created a 4 second long, 60fps, 4K video!

Initial Hello

Simple animations with interpolate

Rendering some static text to video doesn't make for an exciting video, so let's learn how to reach into the state of the application, and adjust the style of our text, depending on the current frame. Here are the 2 hooks we'll use to accomplish this:

  • useCurrentFrame: This hook returns the current frame number.
  • interpolate: This hook helps the user change values throughout several frames. It takes in 3 arguments, the current frame (from useCurrentFrame), a tuple designating the start and end frame for the interpolation, and another tuple defining the start and end values for the frame duration.

Using these 2 hooks, we can define what the opacity of our text should be, as a function of the current frame:

import { FC } from 'react';
import { interpolate, useCurrentFrame } from 'remotion';

type HelloWorldProps = {
 text: string;
};
export const HelloWorld: FC<HelloWorldProps> = ({ text }) => {
 const frame = useCurrentFrame();

 const fadeInTime = 90;
 const startFrame = 30;
 const opacity = interpolate(
  frame,
  [startFrame, startFrame + fadeInTime],
  [0, 1]
 );

 return (
  <div
   style={{
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flex: 1,
    backgroundColor: 'black',
    color: 'cyan',
    fontSize: 200,
    fontFamily: 'Arial, Helvetica, sans-serif',
   }}
  >
   <div style={{ opacity }}>{text}</div>
  </div>
 );
};

Here's a link to what this would render. We aren't rendering static text anymore. Now we have a real video!

The Sequence component

Videos can be made up of MANY moving parts though. Our current video only has 1 moving part, but let's add some complexity to the video and split this into 2 parts. To do this, we need to take advantage of the Sequence component.

Using a Sequence, you can time-shift certain parts of your animation and therefore make them more reusable. Sequences are also shown in the timeline and help you visually understand the structure of your video. -- Remotion Docs

We can use sequences to help us break apart our logic even further, and we can also view these sequences on our composition timeline. Here are some important props for the Sequence component:

  • from (required, number): The frame that the Sequence children start at. The initial frame for them is 0.
  • durationInFrames (optional, default: Infinity): This is how many frames a sequence is displayed.
  • name (optional): Name your sequences to label them in the development environment.
  • layout (optional, default: 'absolute-fill'): Sequences are positioned absolutely initially, use this to handle layout manually.

Let's animate our "Hello, World!" a bit more:

src/components/HelloWorld.tsx

import { FC } from 'react';
import { interpolate, useCurrentFrame, Sequence } from 'remotion';

type HelloWorldProps = {
 firstText: string;
 secondText: string;
};
export const HelloWorld: FC<HelloWorldProps> = ({ firstText, secondText }) => {
 const frame = useCurrentFrame();

 const fadeInTime = 90;

 const firstTextStartFrame = 30;
 const firstTextOpacity = interpolate(
  frame,
  [firstTextStartFrame, firstTextStartFrame + fadeInTime],
  [0, 1]
 );

 const secondTextStartFrame = 150;
 const secondTextOpacity = interpolate(
  frame,
  [secondTextStartFrame, secondTextStartFrame + fadeInTime],
  [0, 1]
 );

 return (
  <div
   style={{
    flex: 1,
    backgroundColor: 'black',
    color: 'cyan',
    fontSize: 200,
    fontFamily: 'Arial, Helvetica, sans-serif',
   }}
  >
   <Sequence name="FirstText" from={0}>
    <div
     style={{
      position: 'absolute',
      opacity: firstTextOpacity,
      top: 250,
      left: '50%',
      transform: 'translateX(-50%)',
     }}
    >
     {firstText}
    </div>
   </Sequence>
   <Sequence name="SecondText" from={150}>
    <div
     style={{
      position: 'absolute',
      opacity: secondTextOpacity,
      margin: '0 auto',
      top: 550,
      left: '50%',
      transform: 'translateX(-50%)',
     }}
    >
     {secondText}
    </div>
   </Sequence>
  </div>
 );
};
New Hello

With a few sequences, we are now separating our animations, and labeling them in our dev environment. Here is the render uploaded to YouTube.

Putting the 'World' in 'Hello, World!'

Being able to render text is amazing, but in the animation world, models aren't drawn manually with code. Instead, they're generated using software, such as Blender for example. One of the tools we can render with using Remotion is React Three Fiber. Just like Remotion lets us render to a Video format, React Three Fiber allows us to render React components into ThreeJS, a WebGL library.

With a bit of React Three Fiber logic and a publicly available model of our planet, we can put the 'World' in 'Hello, World!'

Hello, World!

Completed Hello World

Going into how to work with React Three Fiber is a bit out of the scope of this post, but I wanted to show just what the developer is capable of with Remotion, and rendering a spinning planet with JSX is pretty amazing.

src/components/HelloWorld.tsx

   <Sequence name="Earth" from={mainTextFadeOutFrame + mainTextFadeOutTime}>
    <div style={{ opacity: earthOpacity }}>
     <ThreeCanvas width={1920} height={1080}>
      <ambientLight />
      <pointLight position={[10, 10, 10]} />
      <Suspense fallback={null}>
       <Earth scale={0.005} />
      </Suspense>
     </ThreeCanvas>
    </div>
   </Sequence>

Conclusion

React truly is a "Learn Once, Write Anywhere" language. The DOM is nowhere close to the only render target React is capable of. With Remotion, we've learned how to slap together some JSX and hooks, and render actual videos, programmatically. Feel free to clone the repo and play around with the logic.

If you've followed along with this post, you can now proudly state that you can literally render "Hello, World!" with React!