packages/docs/docs/interactive-with-schema.mdx
Wraps a React component so Remotion Studio can expose its props as timeline controls.
Use it when you build a component that should behave like <Sequence>, <Solid> or <CanvasImage>: The component appears in the timeline, can expose editable controls and can receive saved values from the Studio.
import {forwardRef, useImperativeHandle, useRef} from 'react';
import {
Interactive,
Sequence,
type InteractiveBaseProps,
type InteractiveTransformProps,
type SequenceControls,
type InteractivitySchema,
} from 'remotion';
type CircleProps = InteractiveBaseProps &
InteractiveTransformProps & {
readonly radius?: number;
readonly color?: string;
};
const circleSchema = {
...Interactive.baseSchema,
radius: {
type: 'number',
min: 1,
step: 1,
default: 80,
description: 'Radius',
hiddenFromList: false,
},
color: {
type: 'color',
default: '#0b84ff',
description: 'Color',
},
...Interactive.transformSchema,
} as const satisfies InteractivitySchema;
const CircleInner = forwardRef<
HTMLDivElement,
CircleProps & {
readonly controls: SequenceControls | undefined;
}
>(
(
{
radius = 80,
color = '#0b84ff',
name,
style,
controls,
...sequenceProps
},
ref,
) => {
const outlineRef = useRef<HTMLDivElement>(null);
useImperativeHandle(ref, () => outlineRef.current as HTMLDivElement, []);
return (
<Sequence
layout="none"
{...sequenceProps}
name={name ?? '<Circle>'}
controls={controls}
outlineRef={outlineRef}
>
<div
ref={outlineRef}
style={{
...style,
width: radius * 2,
height: radius * 2,
borderRadius: '50%',
backgroundColor: color,
}}
/>
</Sequence>
);
},
);
export const Circle = Interactive.withSchema({
Component: CircleInner,
componentName: '<Circle>',
componentIdentity: 'com.example.Circle',
schema: circleSchema,
supportsEffects: false,
});
The exported Circle component only accepts CircleProps. The controls prop is passed by Interactive.withSchema() to the inner component and should be forwarded to the <Sequence> that represents the component in the timeline.
If you use <Sequence layout="none">, pass outlineRef so the Studio can draw a canvas outline around the rendered element.
import {Interactive} from 'remotion';
Call Interactive.withSchema() with an options object.
ComponentThe component to wrap.
It must accept its public props plus controls.
controlsThe control state created by Interactive.withSchema().
Forward it to the <Sequence> that registers the component in the timeline.
schemaAn InteractivitySchema that describes which props are editable in Remotion Studio.
Use Interactive.baseSchema for Sequence-like timeline props. Use Interactive.transformSchema if the component accepts a style prop and should expose transform controls. Use Interactive.cropSchema with InteractiveCropProps to opt the component into crop controls.
For reliable source updates, follow Interactivity best practices: Keep editable values inline, use hardcoded interpolate() keyframe arrays, use translate, scale, rotate and opacity directly, and avoid animated layout props or transform strings.
componentIdentityA stable string that identifies the component type.
Use reverse-DNS style names such as "com.example.Circle". Pass null if the component should not be identified for source updates.
supportsEffectsSet to true if this component supports an effects prop. Otherwise, set it to false.
Returns a component with the public props of Component.
During rendering, in the Player and in read-only Studio sessions, Interactive.withSchema() renders the component with the original props.
In editable Studio sessions, it reads the current prop values, applies timeline overrides and passes the merged props to the inner component.