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 id for 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:

  1. Retrieves the selected composition from the manager
  2. Validates dimensions, fps, and duration
  3. Merges default props with editor-provided overrides
  4. Returns a VideoConfigState object indicating success, loading, or error
// 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:

  1. Mount: InnerComposition registers "MainVideo" with the CompositionManager
  2. Resolution: When rendering starts, useResolvedVideoConfig("MainVideo") validates the 1920x1080 configuration and merges props
  3. Render: The engine creates a portal containing <MyScene title="Hello World" /> using the appropriate fallback for the current environment
  4. Cleanup: If the component unmounts, unregisterComposition("MainVideo") removes it from the global registry

Summary

  • Registration Phase: The <Composition> component registers itself with the global CompositionManager via registerComposition in packages/core/src/Composition.tsx, storing metadata like dimensions, fps, and the component reference.

  • State Management: The CompositionManagerProvider maintains the registry in React state and exposes mutation functions through the CompositionSetters context, enabling dynamic composition management in the Studio UI.

  • Configuration Resolution: Before rendering, useResolvedVideoConfig in packages/core/src/ResolveCompositionConfig.tsx validates video parameters and merges default props with overrides, returning a structured VideoConfigState.

  • 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 unregisterComposition to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →