Skip to main content

Composing components

React is perfect for reusing, parametrizing, and composing components with multiple levels of depth.
You can use the regular React primitives for this.

Start with a component that accepts children and a color. Reuse it with different props:

MyVideo.tsx
import type {CSSProperties, ReactNode} from 'react'; type TitleProps = { readonly children: ReactNode; readonly color: string; readonly style?: CSSProperties; }; const Title = ({children, color, style}: TitleProps) => ( <div style={{ fontFamily: 'Arial, sans-serif', fontSize: 80, textAlign: 'center', color, ...style, }} > {children} </div> ); export const MyVideo = () => ( <> <Title color="#0b84f3">Hello</Title> <Title color="#9b5de5">World</Title> </> );

Both titles are visible at the same time. Changing the component updates every instance; changing one instance's props only affects that instance.

Add timing primitives​

Use Interactive.withSchema() with wrapInSequence: true to add timing props.
Rename the function to TitleInner, and export the wrapper:

Title.tsx
export const Title = Interactive.withSchema({ Component: TitleInner, componentName: 'Title', schema: {}, wrapInSequence: true, });

Forward the style prop to the outer <div> as shown in the first example.
The wrapper uses it to apply transforms and other visual edits.

You can now put the titles on the timeline by passing from and durationInFrames directly:

MyVideo.tsx
import {useVideoConfig} from 'remotion'; import {Title} from './Title'; export const MyVideo = () => { const {fps} = useVideoConfig(); return ( <> <Title color="#0b84f3" durationInFrames={2 * fps}> Hello </Title> <Title color="#9b5de5" from={2 * fps} durationInFrames={2 * fps}> World </Title> </> ); };

Add parameterization interactivity​

The props already let you parameterize the component in code.
Describe them in the schema to make them editable in the Remotion Studio:

Title.tsx
export const Title = Interactive.withSchema({ Component: TitleInner, componentName: 'Title', schema: { ...Interactive.childrenSchema, color: {type: 'color', default: '#0b84f3', description: 'Text color'}, }, wrapInSequence: true, });

Interactive.childrenSchema adds an editable text field, and color adds a color picker in the Studio.

Connected compositions​

If a component is also registered as a composition, it gets special treatment in the Remotion Studio.

Instead of repeating all children, only a single item is being displayed.
You can double-click an item to jump to the composition and edit the children on the composition's own timeline.

Two Title tracks in the Studio timeline, each with the connected composition icon next to its name.

If you are familiar with After Effects, this is a similar concept to "Pre-Composing" layers, except that in Remotion, they are also parameterizable.

To set up a connected composition, we recommend you base it off a absolute-fill layout:

Title.tsx
import type {ReactNode} from 'react'; import {Interactive} from 'remotion'; type TitleProps = { readonly children: ReactNode; readonly color: string; }; const TitleInner = ({children, color}: TitleProps) => ( <div style={{ fontFamily: 'Arial, sans-serif', fontSize: 80, textAlign: 'center', color, }} > {children} </div> ); export const Title = Interactive.withSchema({ Component: TitleInner, componentName: 'Title', schema: { ...Interactive.childrenSchema, color: {type: 'color', default: '#0b84f3', description: 'Text color'}, }, wrapInSequence: true, layout: 'absolute-fill', });

and add a <Composition> registering your component:

Root.tsx
import {Composition} from 'remotion'; import {Title} from './Title'; <Composition id="Title" component={Title} width={1280} height={720} fps={30} durationInFrames={60} defaultProps={{children: 'Hello World!', color: '#0b84f3'}} />

You need to set a fixed width, height, and frame rate for the composition, even if it is different than the parent video's settings.

See also​