{"id":"8f230b51-1719-495c-ab96-bd6b909b0340","slug":"compositions","kind":"skill","name":"compositions","summary":"Defining compositions, stills, folders, default props and dynamic metadata","owner":"vercel-labs","repoUrl":"https://github.com/vercel-labs/json-render/blob/HEAD/skills/remotion-best-practices/rules/compositions.md","license":"Apache-2.0","licenseTier":"permissive","trust":"official","stars":18658,"match":null,"fewStars":false,"format":"instructions","createdAt":null,"addedAt":"2026-10-14T10:59:30.683Z","logoUrl":"https://avatars.githubusercontent.com/u/108547162?v=4","linkOnly":false,"collection":{"name":"json-render","repo":"vercel-labs/json-render"},"skillPath":"skills/remotion-best-practices/rules/compositions.md","categories":["writing"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-14T10:59:30.682Z","sample":false,"url":"https://conduits.dev/tools/compositions","language":"TypeScript","transports":[],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"A `<Composition>` defines the component, width, height, fps and duration of a renderable video.\n\nIt normally is placed in the `src/Root.tsx` file.\n\n```tsx\nimport {Composition} from 'remotion';\nimport {MyComposition} from './MyComposition';\n\nexport const RemotionRoot = () => {\n  return <Composition id=\"MyComposition\" component={MyComposition} durationInFrames={100} fps={30} width={1080} height={1080} />;\n};\n```\n\n## Default Props\n\nPass `defaultProps` to provide initial values for your component.  \nValues must be JSON-serializable (`Date`, `Map`, `Set`, and `staticFile()` are supported).\n\n```tsx\nimport {Composition} from 'remotion';\nimport {MyComposition, MyCompositionProps} from './MyComposition';\n\nexport const RemotionRoot = () => {\n  return (\n    <Composition\n      id=\"MyComposition\"\n      component={MyComposition}\n      durationInFrames={100}\n      fps={30}\n      width={1080}\n      height={1080}\n      defaultProps={\n        {\n          title: 'Hello World',\n          color: '#ff0000',\n        } satisfies MyCompositionProps\n      }\n    />\n  );\n};\n```\n\nUse `type` declarations for props rather than `interface` to ensure `defaultProps` type safety.\n\n## Folders\n\nUse `<Folder>` to organize compositions in the sidebar.  \nFolder names can only contain letters, numbers, and hyphens.\n\n```tsx\nimport {Composition, Folder} from 'remotion';\n\nexport const RemotionRoot = () => {\n  return (\n    <>\n      <Folder name=\"Marketing\">\n        <Composition id=\"Promo\" /* ... */ />\n        <Composition id=\"Ad\" /* ... */ />\n      </Folder>\n      <Folder name=\"Social\">\n        <Folder name=\"Instagram\">\n          <Composition id=\"Story\" /* ... */ />\n          <Composition id=\"Reel\" /* ... */ />\n        </Folder>\n      </Folder>\n    </>\n  );\n};\n```\n\n## Stills\n\nUse `<Still>` for single-frame images. It does not require `durationInFrames` or `fps`.\n\n```tsx\nimport {Still} from 'remotion';\nimport {Thumbnail} from './Thumbnail';\n\nexport const RemotionRoot = () => {\n  return <Still id=\"Thumbnail\" component={Thumbnail} width={1280} height={720} />;\n};\n```\n\n## Calculate Metadata\n\nUse `calculateMetadata` to make dimensions, duration, or props dynamic based on data.\n\n```tsx\nimport {Composition, CalculateMetadataFunction} from 'remotion';\nimport {MyComposition, MyCompositionProps} from './MyComposition';\n\nconst calculateMetadata: CalculateMetadataFunction<MyCompositionProps> = async ({props, abortSignal}) => {\n  const data = await fetch(`https://api.example.com/video/${props.videoId}`, {\n    signal: abortSignal,\n  }).then((res) => res.json());\n\n  return {\n    durationInFrames: Math.ceil(data.duration * 30),\n    props: {\n      ...props,\n      videoUrl: data.url,\n    },\n  };\n};\n\nexport const RemotionRoot = () => {\n  return (\n    <Composition\n      id=\"MyComposition\"\n      component={MyComposition}\n      durationInFrames={100} // Placeholder, will be overridden\n      fps={30}\n      width={1080}\n      height={1080}\n      defaultProps={{videoId: 'abc123'}}\n      calculateMetadata={calculateMetadata}\n    />\n  );\n};\n```\n\nThe function can return `props`, `durationInFrames`, `width`, `height`, `fps`, and codec-related defaults. It runs once before rendering begins.\n\n## Nesting compositions within another\n\nTo add a composition within another composition, you can use the `<Sequence>` component with a `width` and `height` prop to specify the size of the composition.\n\n```tsx\n<AbsoluteFill>\n  <Sequence width={COMPOSITION_WIDTH} height={COMPOSITION_HEIGHT}>\n    <CompositionComponent />\n  </Sequence>\n</AbsoluteFill>\n```"}