Skip to main content

Make a component interactivev4.0.479

Interactive components can be selected in the Remotion Studio timeline and can expose editable props.

If you only need an editable HTML or SVG element, use Interactive. For a custom component, wrap it with Interactive.withSchema().

Create a schema

An InteractivitySchema describes which props the Studio may edit.

Use Interactive.baseSchema for props inherited from <Sequence>, such as from, durationInFrames, freeze, hidden, name and showInTimeline.

Use Interactive.transformSchema if your component accepts a style prop and applies it to the rendered element.

Badge.tsx
import type React from 'react'; import { Interactive, type InteractiveBaseProps, type InteractiveTransformProps, type InteractivitySchema, } from 'remotion'; type BadgeProps = InteractiveBaseProps & InteractiveTransformProps & { readonly children?: React.ReactNode; readonly color?: string; readonly padding?: number; }; const badgeSchema = { ...Interactive.baseSchema, color: { type: 'color', default: '#0b84ff', description: 'Color', }, padding: { type: 'number', min: 0, step: 1, default: 16, description: 'Padding', hiddenFromList: false, }, ...Interactive.transformSchema, } as const satisfies InteractivitySchema;

Forward controls

The inner component receives a controls prop from Interactive.withSchema().

Forward it to the <Sequence> that represents your component in the timeline.

Badge.tsx
const BadgeInner = forwardRef< HTMLDivElement, BadgeProps & { readonly controls: SequenceControls | undefined; } >( ( { children, color = '#0b84ff', padding = 16, style, name, controls, ...sequenceProps }, ref, ) => { const outlineRef = useRef<HTMLDivElement>(null); useImperativeHandle(ref, () => outlineRef.current as HTMLDivElement, []); return ( <Sequence layout="none" {...sequenceProps} name={name ?? '<Badge>'} controls={controls} outlineRef={outlineRef} > <div ref={outlineRef} style={{ ...style, display: 'inline-flex', borderRadius: 999, backgroundColor: color, color: 'white', fontWeight: 700, padding, }} > {children} </div> </Sequence> ); }, );

The exported component should not expose controls as a public prop.

Pass outlineRef when using <Sequence layout="none"> so the Studio can draw an outline around the rendered element.

Wrap the component

Use a stable componentIdentity so saved Studio edits can be associated with the component.

Badge.tsx
export const Badge = Interactive.withSchema({ Component: BadgeInner, componentIdentity: 'com.example.Badge', schema: badgeSchema, supportsEffects: false, });

Use the component

MyComp.tsx
import {AbsoluteFill} from 'remotion'; import {Badge} from './Badge'; export const MyComp = () => { return ( <AbsoluteFill> <Badge from={30} durationInFrames={90} color="#ff5c7a" style={{translate: '120px 80px'}} > Sale </Badge> </AbsoluteFill> ); };

The component now has a timeline row, inherits the common <Sequence> props and exposes color, padding and transform controls in the Studio.

Choosing schema fragments

Interactive.baseSchema

Use it when the component renders a <Sequence> internally.

Interactive.transformSchema

Use it when the component accepts a style prop and applies it to the rendered element.

Interactive.premountSchema

Use it when the component forwards premountFor, postmountFor, styleWhilePremounted and styleWhilePostmounted to a <Sequence> with layout="absolute-fill".

See also