How Remotion's Composition Lifecycle Works: From Registration to Render
Remotion's Composition lifecycle involves four main phases: registration with the CompositionManager, state management through React context, video configuration resolution via useResolvedVideoConfig, and conditional rendering through portals for Studio or production renders.
The <Composition> component in the remotion-dev/remotion repository serves as the entry point that tells the rendering engine which React component should become a video and how it should be configured. Understanding this lifecycle is essential for debugging registration issues, optimizing render performance, and building custom tooling around Remotion.
The Four Phases of the Remotion Composition Lifecycle
Phase 1: Mount and Registration
When a <Composition> element renders, the internal InnerComposition component (located in packages/core/src/Composition.tsx) executes a useEffect hook that registers the composition with the global CompositionManager.
The registration process stores critical metadata including:
- Unique
idfor the composition - Dimensions (
width,height) - Frame rate (
fps) - Duration in frames
- Default props
- Lazy-loaded component reference
// From Composition.tsx - registration effect
useEffect(() => {
registerComposition({
id,
width,
height,
fps,
durationInFrames,
defaultProps,
component: lazyComponent
});
}, [id, width, height, fps, durationInFrames, defaultProps, lazyComponent]);
Phase 2: State Management
The CompositionManagerProvider (in packages/core/src/CompositionManagerProvider.tsx) maintains the list of registered compositions in React state. This provider exposes setter functions through the CompositionSetters context, allowing runtime registration and unregistration.
Key capabilities include:
- Dynamic registration: Studio UI can add compositions at runtime
- State synchronization: Shared state between Studio and render pipeline
- Validation: Ensures unique IDs and valid video configurations
// CompositionManagerProvider.tsx structure
const [compositions, setCompositions] = useState<CompositionState[]>([]);
const registerComposition = useCallback((composition: CompositionState) => {
setCompositions(prev => [...prev, composition]);
}, []);
const unregisterComposition = useCallback((id: string) => {
setCompositions(prev => prev.filter(c => c.id !== id));
}, []);
Phase 3: Video Configuration Resolution
Before rendering begins, the engine must resolve and validate the video configuration. The useResolvedVideoConfig hook (in packages/core/src/ResolveCompositionConfig.tsx) performs this resolution.
This hook:
- Retrieves the selected composition from the manager
- Validates dimensions, fps, and duration
- Merges default props with editor-provided overrides
- Returns a
VideoConfigStateobject indicatingsuccess,loading, orerror
// Using the resolution hook
import {useResolvedVideoConfig} from 'remotion';
const MyComponent = () => {
const config = useResolvedVideoConfig('MyComposition');
if (config.type === 'success') {
console.log(config.result.durationInFrames);
}
};
Phase 4: Render Execution
The final phase differs based on the execution environment. The InnerComposition component checks environment.isStudio and environment.isRendering to determine the render path.
Studio Mode (environment.isStudio === true):
- Renders inside a portal (
portalNode()) - Displays a
<Loading />fallback while the component initializes - Enables hot reloading and interactive editing
Production Render (environment.isRendering === true):
- Uses the same portal mechanism but with a
<Fallback />component - Delays rendering until the lazy component is fully loaded
- Optimizes for headless rendering performance
// Conditional rendering logic from Composition.tsx
if (environment.isStudio) {
return createPortal(
<Suspense fallback={<Loading />}>
<ActualComponent {...resolvedProps} />
</Suspense>,
portalNode()
);
}
if (environment.isRendering) {
return createPortal(
<Suspense fallback={<Fallback />}>
<ActualComponent {...resolvedProps} />
</Suspense>,
portalNode()
);
}
Cleanup and Unmounting
When a <Composition> unmounts—whether through hot module replacement, navigation changes, or component tree updates—the cleanup function returned from the registration useEffect executes unregisterComposition.
This process:
- Removes the composition entry from the manager's state array
- Frees memory associated with the component reference
- Prevents stale configurations from affecting subsequent renders
// Cleanup from Composition.tsx
useEffect(() => {
registerComposition(compositionData);
return () => {
unregisterComposition(id);
};
}, [id, /* other deps */]);
Key Files in the Composition Lifecycle
Understanding the source structure helps when debugging registration issues or extending Remotion's functionality:
| File | Role |
|---|---|
packages/core/src/Composition.tsx |
Exported <Composition> component; handles registration, unregistration, and conditional portal rendering |
packages/core/src/CompositionManagerProvider.tsx |
Global state provider for composition registry; exposes setter functions |
packages/core/src/CompositionManagerContext.tsx |
Context definitions for read-only manager access and mutable setters |
packages/core/src/ResolveCompositionConfig.tsx |
Validates and resolves video configurations via useResolvedVideoConfig |
packages/core/src/CompositionManager.tsx |
Type definitions and refs used by the manager (e.g., compositionsRef) |
Practical Implementation Example
Here's a complete example showing how the lifecycle works in practice:
// MyVideo.tsx
import {Composition} from 'remotion';
import {MyScene} from './MyScene';
export const MyVideo = () => {
return (
<Composition
id="MainVideo"
component={MyScene}
width={1920}
height={1080}
fps={30}
durationInFrames={300}
defaultProps={{title: 'Hello World'}}
/>
);
};
When this JSX executes:
- Mount:
InnerCompositionregisters "MainVideo" with theCompositionManager - Resolution: When rendering starts,
useResolvedVideoConfig("MainVideo")validates the 1920x1080 configuration and merges props - Render: The engine creates a portal containing
<MyScene title="Hello World" />using the appropriate fallback for the current environment - Cleanup: If the component unmounts,
unregisterComposition("MainVideo")removes it from the global registry
Summary
-
Registration Phase: The
<Composition>component registers itself with the globalCompositionManagerviaregisterCompositioninpackages/core/src/Composition.tsx, storing metadata like dimensions, fps, and the component reference. -
State Management: The
CompositionManagerProvidermaintains the registry in React state and exposes mutation functions through theCompositionSetterscontext, enabling dynamic composition management in the Studio UI. -
Configuration Resolution: Before rendering,
useResolvedVideoConfiginpackages/core/src/ResolveCompositionConfig.tsxvalidates video parameters and merges default props with overrides, returning a structuredVideoConfigState. -
Environment-Specific Rendering: The system uses React portals to render compositions differently in Studio mode (with
<Loading />fallback) versus production rendering (with<Fallback />), ensuring proper initialization before the actual component executes. -
Cleanup: Unmounting triggers
unregisterCompositionto remove the composition from global state, preventing memory leaks and stale configuration issues.
Frequently Asked Questions
How does Remotion handle composition registration during fast refresh?
During development with React Fast Refresh, components may remount frequently. The useEffect hook in InnerComposition handles this by calling unregisterComposition in its cleanup function and immediately re-registering with the same ID. This ensures the CompositionManager always has the latest component reference and props without duplicate entries.
Can I dynamically add or remove compositions at runtime?
Yes, through the CompositionSetters context exposed by CompositionManagerProvider. The Studio UI uses these setters to manage compositions dynamically. You can import useContext(CompositionSetters) and call registerComposition or unregisterComposition programmatically, though this is primarily intended for advanced tooling rather than typical video components.
What happens if video configuration validation fails?
The useResolvedVideoConfig hook returns a VideoConfigState with a type property indicating success, loading, or error. If validation fails (for example, if dimensions are invalid or the composition ID doesn't exist), the hook returns an error state with a descriptive message. The rendering engine checks this state and prevents invalid configurations from reaching the render pipeline.
How does Remotion differentiate between Studio and production rendering environments?
The system checks environment.isStudio and environment.isRendering boolean flags. In Studio mode (isStudio), compositions render with a <Loading /> fallback that supports hot reloading and interactive editing. In production rendering (isRendering), the same portal mechanism uses a <Fallback /> component optimized for headless execution, ensuring the component is fully loaded before the render begins.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →