OfficialVídeo e Animaçãogithub.com

remotion-interactivity

Estrutura a marcação Remotion para interatividade

O que é remotion-interactivity?

Esta skill guia o desenvolvimento de componentes Remotion que se tornam interativos no Remotion Studio, permitindo seleção, arrastar e soltar, redimensionamento, rotação e edição visual de estilos CSS, keyframes e valores de easing. Ela explica como estruturar o código JSX e os schemas de props para que o Studio reconheça e permita a manipulação gráfica de cada elemento. A skill aborda o uso de `Interactive.withSchema()` para componentes com suas próprias timelines, a escolha adequada de tipos de campo no schema (como string, number, color, enum, asset), e a importância de manter estilos CSS inline e animações com `interpolate()` para garantir a editabilidade. Também cobre o registro de componentes reutilizáveis como "connected compositions", a colocação direta de props de temporização nos componentes, e a criação de nós JSX separados para cada item editável independente. A documentação é voltada para desenvolvedores que usam Remotion e desejam criar componentes ricos e editáveis visualmente no Studio.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/remotion-dev/skills/tree/main/skills/remotion-interactivity

Installed? Explore more Vídeo e Animação skills: steipete/video-frames, steipete/summarize, openclaw/openclaw · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

remotion-interactivity preview

Documentação

Ao escrever a marcação Remotion de uma maneira específica, o Remotion Studio consegue reconhecer a estrutura do código e torná-la interativa:

  • Permitindo que itens sejam selecionados clicando neles
  • Permitindo arrastar e soltar, redimensionar e rotacionar
  • Editando os estilos CSS
  • Tornando keyframes e valores de easing editáveis

Se a marcação for muito complexa para o Studio torná-la interativa, os valores ficarão acinzentados.

Prefira componentes interativos com suas próprias timelines

Use Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) para componentes que preenchem seu pai. O wrapper fornece sua raiz visual e item de timeline, e corta o overflow por padrão, para que o componente interno possa retornar um fragmento. Coloque temporização, premount, dimensões e estilos por instância no componente exportado.

Mantenha visuais que pertencem ao mesmo grupo no mesmo componente, para que se movam e cortem juntos. Um contêiner de layout interno ainda pode fornecer alinhamento, corte dentro de uma área menor ou um grupo animado; um contêiner que apenas preenche a cena geralmente é desnecessário. Cartões, caixas de texto e SVGs com seus próprios limites podem manter o padrão layout: 'none' e encaminhar style para sua raiz visual.

import {Video} from '@remotion/media';
import type React from 'react';
import {Interactive, useVideoConfig, type InteractivitySchema} from 'remotion';

type SceneProps = {
  readonly title: string;
};

const SceneInner: React.FC<SceneProps> = ({title}) => {
  const {fps} = useVideoConfig();

  return (
    <>
      <Video
        name="Background footage"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{width: '100%', height: '100%'}}
      />
      <Interactive.Div
        name="Scene title"
        style={{
          position: 'absolute',
          left: 80,
          bottom: 80,
          color: 'white',
          fontFamily: 'Helvetica, Arial, sans-serif',
          fontSize: 80,
        }}
      >
        {title}
      </Interactive.Div>
    </>
  );
};

const sceneSchema = {
  title: {type: 'string', default: '', description: 'Título'},
} as const satisfies InteractivitySchema;

export const Scene = Interactive.withSchema({
  Component: SceneInner,
  componentName: 'Scene',
  schema: sceneSchema,
  wrapInSequence: true,
  layout: 'absolute-fill',
});

Escolha campos de schema

Cada chave no schema é uma prop que o Studio pode ler e editar no local de chamada. Props que não estão no schema ainda funcionam, mas não são editáveis no Studio. Chaves podem usar notação de ponto, como style.color.

TipoUse paraCom keyframe
stringStrings, como um título, prompt ou trecho de códigoNão
numberNúmeros, com opcionais min, max, step, integerSim
booleanInterruptores liga/desligaSim (segurar)
colorStrings de cor CSSSim
enumUma escolha entre variants, cada uma com seu próprio schema aninhadoOpt-in (segurar)
arrayListas de números, cores, enums e maisNão
assetFontes de mídia, com assetType opcionalNão
font-familyFamília de fonte CSSNão
font-weightPeso da fonteSim
translate, scale, rotation-css, rotation-degrees, transform-originTransformaçõesSim
uv-coordinateUm ponto [x, y] normalizado no elementoSim
svg-pathDados de caminho SVGSim
remotion-captionsDados Caption[]Não
hiddenUm valor mantido fora dos controles—

Todo campo precisa de um default e deve ter uma description, que o Studio mostra como rótulo. Defina keyframable: false para mostrar um controle estático.

Exponha conteúdo e aparência por instância, como o texto e uma cor de destaque. Não adicione campos para style.translate, style.scale, style.rotate, style.transformOrigin e style.opacity: wrapInSequence: true já os adiciona.

Para tornar propriedades CSS da raiz editáveis por instância, espalhe os fragmentos de schema internos:

const cardSchema = {
  ...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
  ...Interactive.backgroundSchema, // style.backgroundColor
  ...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;

Também disponíveis: Interactive.borderSchema, e Interactive.captionsSchema para componentes que aceitam legendas. Para componentes que aceitam filhos React.ReactNode, adicione ...Interactive.childrenSchema. Ele fornece controles para o conteúdo dos filhos e pode evoluir para suportar conteúdo mais rico. Atualmente, usa um campo string rotulado como "Texto" com um padrão de ''. Se um componente exigir filhos apenas de string, declare um campo children explícito com type: 'string'. Interactive.textSchema controla estilos tipográficos; childrenSchema controla o conteúdo. O fragmento de filhos não está incluído em baseSchema ou adicionado automaticamente a componentes personalizados. Marcação aninhada e filhos computados são atualmente somente leitura. text-content é um alias obsoleto para o tipo de campo string. Veja InteractivitySchema para todas as opções.

Registre componentes reutilizáveis como composições conectadas

Registre cenas significativas e componentes reutilizáveis com suas próprias camadas ou animação como composições independentes. O Studio chama estas de composições conectadas: elas podem ser abertas em sua própria timeline enquanto compartilham a mesma implementação de componente com o vídeo pai.

Defina estilos necessários e carregue fontes dentro do componente para que funcione sem seu pai. Veja independência do pai.

Registre a mesma referência de componente exportada que o pai renderiza. Para o exemplo acima, use component={Scene}, não SceneInner ou um wrapper inline. Com wrapInSequence: true, nenhum <Sequence> extra é necessário para a conexão.

import {Composition} from 'remotion';
import {Scene} from './Scene';

export const RemotionRoot = () => (
  <Composition
    id="Scene"
    component={Scene}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={30}
    defaultProps={{
      title: 'A new perspective',
    }}
  />
);

Escolha um ID único, padrões inline representativos e dimensões, fps e duração adequados para pré-visualizar o componente. Os padrões de registro não copiam automaticamente props de uma instância pai. Veja composições conectadas para detalhes de registro e extração.

Coloque a temporização diretamente no componente

Evite um <Sequence> separado em torno de um componente que já aceita props de temporização. Componentes interativos internos e componentes personalizados feitos com wrapInSequence: true podem receber name, from, durationInFrames, trimBefore, playbackRate e premountFor diretamente.

<Scene
  name="Opening scene"
  from={30}
  durationInFrames={90}
  premountFor={fps}
  style={{opacity: 0.9}}
  title="A new perspective"
/>

Ao remover uma sequência redundante, mova sua temporização e nome para o filho. Preserve qualquer temporização existente no filho; não sobrescreva ou aplique duplamente os deslocamentos. Uma chamada useCurrentFrame() dentro do componente lê seu quadro local, enquanto uma expressão de estilo no local de chamada ainda usa o quadro do chamador. Preserve essa distinção ao mover estilos de animação.

Mantenha um <Sequence> quando ele fornecer temporização compartilhada para vários irmãos, envolver um componente que não lida com temporização, ou fornecer dimensões que o filho não pode aceitar diretamente. Raízes gerenciadas também aceitam width e height para useVideoConfig(). Mantenha <TransitionSeries.Sequence> para layout consecutivo, com ou sem transições; props from diretas não substituem esses comportamentos.

Dê a cada item editável independente seu próprio nó JSX

O Studio edita o nó fonte JSX que criou um item. Se vários itens em tempo de execução vêm do mesmo nó JSX, eles compartilham um único alvo de edição de fonte.

Para cada registro de composição, clipe, cena, camada ou sequência que deve ser editável por conta própria, escreva um nó JSX separado e mantenha suas props editáveis nesse nó. Isso se aplica a <Composition>, <Still>, componentes de mídia internos, <Sequence>, <TransitionSeries.Sequence> e componentes personalizados.

Por exemplo, crie uma timeline editável assim:

import { TransitionSeries } from "@remotion/transitions";

<TransitionSeries>
  <TransitionSeries.Sequence name="Introduction" durationInFrames={90}>
    <Introduction />
  </TransitionSeries.Sequence>
  <TransitionSeries.Sequence name="Demo" durationInFrames={150}>
    <Demo />
  </TransitionSeries.Sequence>
</TransitionSeries>

Um .map() ou outro loop programático criaria vários itens em tempo de execução a partir de um único nó fonte JSX. Isso é apropriado para saída repetida que é intencionalmente controlada como um modelo único, como barras de visualização ou partículas. Não é apropriado quando as instâncias precisam de IDs ou nomes, props, metadados, temporização, ordenação, exclusão ou duplicação independentes no Studio.

Torne um elemento HTML interativo usando Interactive

Todo elemento HTML e SVG (exceto <Img>, que já é interativo) como <div> pode ser tornado interativo usando Interactive:

<Interactive.Div
  name="Cartão de saudação"
  style={{fontSize: 80, padding: 24}}
>
  Olá
</Interactive.Div>

Isso permite que estilos e keyframes sejam definidos no Studio. Seja sensato; se um componente tem muitos elementos, a timeline pode ficar bagunçada.

Prefira texto inline

Se o texto é fixo e usado apenas uma vez, escreva-o diretamente dentro do elemento interativo em vez de extraí-lo para uma constante.

// 👍 Texto fixo permanece editável
<Interactive.Div name="Título">
  Melhores Práticas Remotion
</Interactive.Div>

Use uma prop ou variável apenas quando o texto é dinâmico ou reutilizado.

Dê aos elementos interativos um nome descritivo

Adicione uma prop name aos elementos para torná-los facilmente identificáveis. Evite nomes computados; codifique-os.

<>
  <Interactive.Div name="Título do herói" style={{fontSize: 80}}>
    Dia de lançamento
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Título">
    Dia de lançamento
  </Interactive.Div>
</>

Mantenha todos os estilos CSS inline

A melhor maneira é apenas passar um objeto simples para style - sem referência a constantes, sem espalhamento de objeto, sem matemática.

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Olá Mundo!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ Estilos não inline não são suportados
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ Espalhamento não é suportado
    color: RED, // ❌ Referência a constantes não é suportada
    scale: frame * 10 // ❌ Matemática não é suportada
  }}
>
  Olá Mundo!
</Interactive.Div>

Anime usando interpolate()

Escreva animações como chamadas interpolate() inline na propriedade que muda. O intervalo de saída, easing, extrapolação e a propriedade output devem usar valores codificados.

O intervalo de entrada pode adicionalmente usar durationInFrames, fps, width e height desestruturados diretamente de useVideoConfig(). Identificadores simples como durationInFrames, multiplicação por um número como 2 * fps ou fps * 2, e subtração de um número como durationInFrames - 1 são suportados.

const {fps, durationInFrames} = useVideoConfig();

// 👍 Valores inline podem ser padronizados e ter keyframes
<Interactive.Div
  name="Cartão de produto"
  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}),
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
>
  Produto em destaque
</Interactive.Div>

Chame interpolate como uma expressão inline na propriedade que você deseja animar. Para valores não numéricos como rotação, use a sintaxe de array.

style={{
  transform: `translateY(${interpolate(
    frame,
    [0, 30],
    [-100, 0],
    {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}
  )}px)`
}}

Quando uma propriedade não pode ser decomposta em sub-propriedades editáveis, e você deseja que esses valores apareçam no Studio, use remotion-animation-name ou defina todo o valor de transformação.

Evite mixins de estilo

Mantendo os estilos inline, evite misturá-los com folhas de estilo CSS externas ou a prop className. Se você precisar aplicar estilos de uma folha de estilo, traga-os para o objeto style.

Apenas uma declaração por propriedade

Declare cada propriedade apenas uma vez no mesmo elemento. Se você declarar a mesma propriedade duas vezes no mesmo objeto style, o Studio só poderá editar a última declaração.

Use sub-propriedades

Use uma única propriedade de transformação em vez da sintaxe abreviada transform. O Remotion Studio permite editar visualmente: translate, scale, rotate, transformOrigin, opacity, filter e boxShadow. Use exatamente estas propriedades.

// 👍 Bom: sub-propriedades são editáveis individualmente
<Interactive.Div
  name="Cartão"
  style={{
    translate: '100px 200px',
    scale: 1.5,
    rotate: '15deg',
    opacity: 0.8,
  }}
>
  Conteúdo
</Interactive.Div>
<Interactive.Div
  name="Cartão"
  style={{
    transform: 'translate(100px, 200px) scale(1.5) rotate(15deg)',
    opacity: 0.8,
  }}
>
  Conteúdo
</Interactive.Div>

As propriedades translate, scale e rotate usam os mesmos valores que a função CSS transform. Use unidades apropriadas ao especificar valores: px, %, deg, rad, turn, sem unidade para escala, e assim por diante.

Use "Interactivity Schema" para adicionar controles de propriedade

Um Interactivity Schema permite definir quais props de um componente o Studio deve expor no painel de propriedades. Isso transforma componentes em blocos de construção visuais que os usuários podem configurar sem escrever código.

Para criar um schema, use Interactive.withSchema().

Para detalhes completos, veja a documentação do Interactivity Schema.

Evite classes CSS

Classes CSS não são suportadas para interatividade. Use objetos style inline em vez de classes.

// 👍 Bom: Estilo inline
<Interactive.Div
  name="Botão"
  style={{backgroundColor: 'blue', color: 'white'}}
>
  Clique aqui
</Interactive.Div>
// Não faça isso
<Interactive.Div className="btn-primary" name="Botão">
  Clique aqui
</Interactive.Div>

Use a prop "name" para identificar elementos

A prop name é crucial para o Studio identificar e exibir elementos. Sempre adicione um nome descritivo e único a elementos interativos.

<Interactive.Div
  name="Cartão de destaque"
  style={{
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 16,
  }}
>
  Destaque do vídeo
</Interactive.Div>

Nós JSX aninhados com a mesma prop name também são suportados, mas é melhor usar nomes únicos para clareza.

Evite lógica condicional complexa no JSX

Mantenha a estrutura JSX o mais plana e previsível possível para que o Studio possa interpretá-la. Expressões ternárias ou operadores lógicos com && para renderização condicional podem tornar a marcação não editável.

// Evite isso se a condição não for necessária
<>
  {isVisible && <Interactive.Div name="Elemento">Conteúdo</Interactive.Div>}
</>

Apenas um nível de profundidade para Interactive.Div

Evite aninhar Interactive.Div dentro de Interactive.Div diretamente. Se você precisar de vários elementos interativos, coloque-os no mesmo nível.

<>
  <Interactive.Div name="Primeiro">
    Conteúdo do primeiro
  </Interactive.Div>
  <Interactive.Div name="Segundo">
    Conteúdo do segundo
  </Interactive.Div>
</>

Dicas de desempenho

  • Use premountFor para pré-carregar mídia e componentes pesados
  • Prefira wrapInSequence: true para componentes que têm sua própria temporização
  • Evite re-renderizações desnecessárias mantendo as props estáveis

Recursos adicionais

Individual skills in this repo

This repo contains 19 individual skills — each has its own dedicated page.

Habilidades Relacionadas