Skip to content
Development
Skill

/remotion-interactivity

Structure Remotion markup for interactivity

From plugin
remotion-dev-skills
4.2k12 skills
Install
$ npx -y skills add remotion-dev/skills --skill remotion-interactivity --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition โ†’
  • You can call itInvoke it directly when you want it.
  • Slash command/remotion-interactivity

Context preview

The summary Claude sees to decide when to auto-load this skill.

Structure Remotion markup for interactivity

SKILL.md

remotion-interactivity.SKILL.md
name: remotion-interactivity
description: Structure Remotion markup for interactivity
version: 4.0.507

By writing Remotion markup in a specific way, the Remotion Studio is able to recognize the structure of the code and makes it interactive:

  • Allowing items to be selected by clicking on them
  • Allowing drag+drop, resizing and rotation
  • Editing the CSS styles
  • Making keyframes and easing values editable

If the markup is too complex for the Studio to make it interactive, then the values become grayed out.

Make an HTML element interactive using `Interactive`

Every HTML and SVG element (except `<Img>`, it already is interactive) such as `<div>` can be turned interactive using `Interactive`:

<Interactive.Div
  name="Greeting card"
  style={{fontSize: 80, padding: 24}}
>
  Hello
</Interactive.Div>

This allows styles and keyframes to be set in the Studio. Be sensible, if a component has many elements, the timeline might get messy.

Prefer inline text

If text is fixed and only used once, write it directly inside the interactive element instead of extracting it into a constant.

// ๐Ÿ‘ Fixed copy stays editable
<Interactive.Div name="Title">
  Remotion Best Practices
</Interactive.Div>

Use a prop or variable only when the text is dynamic or reused.

Give interactive elements a descriptive name

Add a `name` prop to elements to make them easily identifyable. Avoid computed names, hardcode them.

<>
  <Interactive.Div name="Hero title" style={{fontSize: 80}}>
    Launch day
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Sequence name="Title">
    Launch day
  </Sequence>
</>

Keep all CSS styles inline

The best way is to just pass a plain object to `style` - no referring to constants, no object spreading, no math.

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hello World!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // โŒ Non-inline styles are not supported
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // โŒ Spreading is not supported
    color: RED, // โŒ Referring to constants is not supported
    scale: frame * 10 // โŒ Math is not supported
  }}
>
  Hello World!
</Interactive.Div>

Animate using `interpolate()`

Write animations as inline `interpolate()` calls on the property that changes. The output range, easing, extrapolation and `output` property should use hardcoded values.

The input range may additionally use `durationInFrames`, `fps`, `width` and `height` destructured directly from `useVideoConfig()`. Bare identifiers such as `durationInFrames`, multiplication with a number such as `2 * fps` or `fps * 2`, and subtraction of a number such as `durationInFrames - 1` are supported.

const {fps, durationInFrames} = useVideoConfig();

// ๐Ÿ‘ Inline values can be standardized and keyframed
<Interactive.Div
  name="Product card"
  style={{
    color: 'white',
    fontSize: 80,
    scale: interpolate(frame, [0, fps], [0, 1], {
      easing: Easing.spring({damping: 200}),
      output: 'perceptual-scale',
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], {
      easing: Easing.spring({damping: 200}),
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    translate: interpolate(
      frame,
      [durationInFrames - 30, durationInFrames],
      ['0px 0px', '0px 120px'],
      {
        easing: Easing.spring({damping: 200}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // โŒ Math should be directly in the markup

<Interactive.Div
  name="Product card"
  style={{
    translate: translateY, // โŒ Only inline interpolate() calls are supported,
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // โŒ Cannot use math with arbitrary variables, cannot use constants
    scale: interpolate(anyVariable, [0, 30], [0, 1]) // โŒ Can only interpret the `frame` variable.
  }}
/>

Use `scale`, `translate`, `rotate` CSS properties

Avoid the `transform` CSS property. If possible, use `scale`, `rotate` and `translate` instead because only they are interactively editable.

Keep composition metadata inline

When scaffolding a composition, keep `width`, `height`, `fps`, `durationInFrames` and `defaultProps` inline and make no type assertions.

The Props editor can save visual edits back to your code when `defaultProps` is an inline object literal on `<Composition>` or `<Still>`.

// ๐Ÿ‘ Static values are in <Composition>, dynamic values are in calculateMetadata()
const calculateMetadata = useMemo(async () => {
  const dimensions = await getDimensions(); // just an example
  return {width: dimensions.width, height: dimensions.height};
});

<Composition
  id="my-video"
  component={MyComponent}
  durationInFrames={150}
  fps={30}
  calculateMetadata={calculateMetadata}
  defaultProps={{title: 'Hello', color: '#0b84ff'}}
/>
const defaultProps = {title: 'Hello', color: '#0b84ff'}; // โŒ Don't extract defaultProps, must be inline
const calculateMetadata = useMemo(() => {
  // โŒ Unnecessary because no calculation is being done,
  return {durationInFrames: 150, fps: 30, width: 1920, height: 1080};
});

<Composition
  id="my-video"
  component={MyComponent}
  calculateMetadata={calculateMetadata}
  defaultProps={{
    title: 'Hello',
  } as Props} // โŒ Don't have type assertions, instead type MyComponent correctly
/>

Use only `calculateMetadat

Read more
Ships withremotion-dev-skills

Remotion maintains a list of Agent Skills that define best practices for working in Remotion projects. These skills are useful for AI agents like Claude Code, Codex, Kimi Code or Cursor.

Get the whole plugin