Remotion: создание видео на React

16 октября 2026
Автор

Антон Ларичев

Что такое Remotion

Remotion — это фреймворк для программного создания видео с использованием React. Вместо видеоредактора вы описываете каждый кадр как React-компонент, используете привычные CSS-свойства для анимаций и рендерите итоговый файл через командную строку.

Подход открывает несколько практических сценариев:

  • генерация персонализированных видео (отчёты, дипломы, поздравления)
  • создание анимированных баннеров и рекламных роликов
  • автоматизация производства контента для социальных сетей
  • визуализация данных в формате видео

Remotion использует Chromium под капотом: каждый кадр — это снимок (скриншот) React-дерева при определённом значении времени. Итоговые кадры склеиваются через FFmpeg в видеофайл.

Курс по теме

Освоить React и Redux Toolkit на курсе

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Установка и первый проект

Создайте новый проект с помощью официального шаблона:

npx create-video@latest my-video
cd my-video
npm install

Запустите студию разработки:

npm start

Студия откроется по адресу http://localhost:3000. Здесь можно воспроизводить видео прямо в браузере, перематывать по кадрам и видеть изменения в реальном времени.

Структура проекта после создания:

my-video/
  src/
    Root.tsx        # точка входа, регистрация композиций
    HelloWorld.tsx  # пример компонента
  remotion.config.ts
  package.json

Ключевые концепции

Composition

Composition — это регистрация видеоролика: его размер, частота кадров и длительность.

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

export const RemotionRoot = () => {
  return (
    <>
      <Composition
        id="MyVideo"
        component={MyVideo}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};

Параметры:

  • id — уникальный идентификатор, используется при рендеринге
  • durationInFrames — длина видео в кадрах (150 кадров при 30 fps = 5 секунд)
  • fps — частота кадров (обычно 24, 30 или 60)
  • width / height — разрешение в пикселях

useCurrentFrame

Главный хук Remotion. Возвращает номер текущего кадра — число от 0 до durationInFrames - 1.

import { useCurrentFrame } from 'remotion';

export const MyVideo = () => {
  const frame = useCurrentFrame();

  return (
    <div style={{ fontSize: 60, color: 'white' }}>
      Кадр: {frame}
    </div>
  );
};

Весь смысл анимации в Remotion строится на этом хуке: вы вычисляете любые CSS-свойства как функцию от frame.

useVideoConfig

Возвращает параметры текущей композиции:

import { useCurrentFrame, useVideoConfig } from 'remotion';

export const MyVideo = () => {
  const frame = useCurrentFrame();
  const { fps, durationInFrames, width, height } = useVideoConfig();

  const progress = frame / durationInFrames;

  return (
    <div style={{
      width,
      height,
      backgroundColor: `hsl(${progress * 360}, 70%, 50%)`
    }} />
  );
};

Анимации с interpolate

Функция interpolate — основной инструмент для создания плавных переходов. Она принимает текущее значение и два диапазона: входной и выходной.

import { useCurrentFrame, interpolate } from 'remotion';

export const FadeIn = () => {
  const frame = useCurrentFrame();

  const opacity = interpolate(
    frame,
    [0, 30],       // входной диапазон: кадры 0–30
    [0, 1],        // выходной диапазон: opacity 0–1
    { extrapolateRight: 'clamp' }
  );

  return (
    <div style={{
      opacity,
      fontSize: 80,
      color: 'white',
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
      height: '100%'
    }}>
      Привет, Remotion!
    </div>
  );
};

Опция extrapolateRight: 'clamp' фиксирует значение после достижения правой границы диапазона — без неё opacity продолжила бы расти за пределы 1.

Несколько анимаций в одном компоненте

import { useCurrentFrame, interpolate } from 'remotion';

export const SlideAndFade = () => {
  const frame = useCurrentFrame();

  const opacity = interpolate(frame, [0, 20], [0, 1], {
    extrapolateRight: 'clamp',
  });

  const translateY = interpolate(frame, [0, 20], [40, 0], {
    extrapolateRight: 'clamp',
  });

  return (
    <div
      style={{
        opacity,
        transform: `translateY(${translateY}px)`,
        fontSize: 64,
        color: 'white',
      }}
    >
      Появление снизу
    </div>
  );
};

spring: физические анимации

Fункция spring создаёт пружинные анимации с физически правдоподобным ускорением и замедлением.

import { useCurrentFrame, useVideoConfig, spring } from 'remotion';

export const SpringDemo = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const scale = spring({
    frame,
    fps,
    config: {
      damping: 10,
      stiffness: 100,
      mass: 0.5,
    },
  });

  return (
    <div
      style={{
        transform: `scale(${scale})`,
        width: 200,
        height: 200,
        backgroundColor: '#7b61ff',
        borderRadius: 20,
      }}
    />
  );
};

Параметры пружины:

  • damping — затухание (чем выше, тем меньше колебаний)
  • stiffness — жёсткость (чем выше, тем быстрее движение)
  • mass — масса объекта (чем выше, тем инертнее)

Sequence: организация сцен

Sequence позволяет запускать дочерние компоненты в определённый момент времени. Внутри Sequence хук useCurrentFrame обнуляется — компонент «не знает», на каком глобальном кадре находится.

import { Sequence } from 'remotion';
import { Title } from './Title';
import { Subtitle } from './Subtitle';
import { Logo } from './Logo';

export const MyVideo = () => {
  return (
    <div style={{ backgroundColor: '#0f0f0f', width: '100%', height: '100%' }}>
      {/* Логотип появляется с самого начала */}
      <Sequence from={0} durationInFrames={150}>
        <Logo />
      </Sequence>

      {/* Заголовок появляется на кадре 10 и длится 60 кадров */}
      <Sequence from={10} durationInFrames={60}>
        <Title />
      </Sequence>

      {/* Подзаголовок появляется на кадре 40 */}
      <Sequence from={40}>
        <Subtitle />
      </Sequence>
    </div>
  );
};

Практический пример: слайд с метриками

Соберём полноценный компонент — анимированный слайд с числовой метрикой:

import {
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  spring,
} from 'remotion';

interface MetricSlideProps {
  label: string;
  value: number;
  unit?: string;
  color?: string;
}

export const MetricSlide: React.FC<MetricSlideProps> = ({
  label,
  value,
  unit = '',
  color = '#7b61ff',
}) => {
  const frame = useCurrentFrame();
  const { fps, durationInFrames } = useVideoConfig();

  const progress = spring({
    frame,
    fps,
    config: { damping: 15, stiffness: 80 },
  });

  const displayedValue = Math.round(progress * value);

  const labelOpacity = interpolate(frame, [10, 30], [0, 1], {
    extrapolateRight: 'clamp',
  });

  const labelY = interpolate(frame, [10, 30], [20, 0], {
    extrapolateRight: 'clamp',
  });

  return (
    <div
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#0f0f0f',
        fontFamily: 'sans-serif',
      }}
    >
      <div
        style={{
          fontSize: 160,
          fontWeight: 700,
          color,
          lineHeight: 1,
        }}
      >
        {displayedValue}{unit}
      </div>

      <div
        style={{
          fontSize: 48,
          color: '#ffffff',
          marginTop: 24,
          opacity: labelOpacity,
          transform: `translateY(${labelY}px)`,
        }}
      >
        {label}
      </div>
    </div>
  );
};

Регистрируем в Root.tsx:

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

export const RemotionRoot = () => (
  <Composition
    id="MetricSlide"
    component={MetricSlide}
    durationInFrames={90}
    fps={30}
    width={1080}
    height={1080}
    defaultProps={{
      label: 'Пользователей в месяц',
      value: 124500,
      unit: '',
      color: '#7b61ff',
    }}
  />
);

Передача данных через props

Компоненты Remotion принимают обычные React-пропсы. При рендеринге их можно передать через JSON-файл или параметры командной строки.

const { value } = getInputProps<{ value: number }>();

Или через defaultProps в Composition для предпросмотра в студии.

Встраивание медиа

Remotion поставляет компоненты Audio, Video и Img с синхронизацией по текущему кадру.

import { Audio, Video, Img, staticFile } from 'remotion';

export const MediaExample = () => (
  <div style={{ width: '100%', height: '100%' }}>
    <Video
      src={staticFile('background.mp4')}
      style={{ width: '100%', height: '100%', objectFit: 'cover' }}
    />
    <Audio src={staticFile('music.mp3')} volume={0.3} />
    <Img
      src={staticFile('logo.png')}
      style={{ position: 'absolute', top: 40, left: 40, width: 120 }}
    />
  </div>
);

Статические файлы размещаются в папке public/ и подключаются через staticFile().

Рендеринг видео

Когда компонент готов, запустите рендеринг:

npx remotion render MetricSlide out/metric.mp4

Для передачи props:

npx remotion render MetricSlide out/metric.mp4 \
  --props='{"value": 99000, "label": "Подписчиков"}'

Параметры рендеринга:

# Изменить кодек
npx remotion render MetricSlide out/metric.webm --codec=vp8

# Только конкретные кадры (для тестирования)
npx remotion render MetricSlide out/metric.mp4 --frames=0-30

# Управление параллелизмом
npx remotion render MetricSlide out/metric.mp4 --concurrency=4

Remotion также поддерживает серверный рендеринг через @remotion/renderer для автоматизации генерации видео в Node.js-приложениях.

import { renderMedia, selectComposition } from '@remotion/renderer';

const composition = await selectComposition({
  serveUrl: 'http://localhost:3000',
  id: 'MetricSlide',
  inputProps: { value: 50000, label: 'Новых клиентов' },
});

await renderMedia({
  composition,
  serveUrl: 'http://localhost:3000',
  codec: 'h264',
  outputLocation: 'out/result.mp4',
});

Советы по производительности

Remotion рендерит каждый кадр независимо, поэтому дорогостоящие вычисления стоит выносить за пределы render-функции:

// Плохо: пересчитывается на каждом кадре
export const Heavy = () => {
  const frame = useCurrentFrame();
  const data = processLargeDataset(); // вызывается на каждом кадре
  return <div>{data[frame]}</div>;
};

// Хорошо: данные вычислены один раз снаружи
const data = processLargeDataset();

export const Heavy = () => {
  const frame = useCurrentFrame();
  return <div>{data[frame]}</div>;
};

Для компонентов, которые не меняются между кадрами, используйте React.memo и useMemo как обычно.

Итог

Remotion переносит привычный React-подход в область создания видео. Если вы умеете писать React-компоненты и CSS-анимации, вы уже обладаете большей частью необходимых знаний. Ключевые строительные блоки:

  • useCurrentFrame — текущий кадр как источник истины для всех анимаций
  • interpolate — линейные переходы между значениями
  • spring — физически правдоподобные пружинные анимации
  • Sequence — временная координация сцен
  • Composition — объявление параметров видео

Чтобы уверенно работать с React и строить сложные UI-компоненты, которые затем можно использовать в Remotion-видео, пройдите курс по React на PurpleSchool: https://purpleschool.ru/course/react?utm_source=knowledgebase&utm_medium=text&utm_campaign=remotion-react-video

Стрелочка влевоServer Components vs Client Components: когда использоватьReact 19: ref как проп без forwardRefСтрелочка вправо

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

React тестирование с Vitest
React с TanStack Router
Zustand: продвинутые паттерны, persist middleware и slicesUncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиReact с WebSocket — реалтайм в компонентахПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаReact Native приложение с нуля до публикации в сторахЛенивая загрузка: как ускорить React-приложение в разыИменование компонентов в React: соглашения и лучшие практикиОшибки гидратации в React и способы их устраненияHOC в React: мастерство композиции компонентовАрхитектура больших React приложений: Feature-Sliced DesignuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
Valtio: атомарный state management в ReactuseState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияХук use() в React 19useEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextНовые возможности React 19Как предотвратить лишние ре-рендеры в React: полное руководствоПравила хуков — правила использованияflushSync — синхронные обновления состояния в ReactuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseMemo vs useCallback: подробное руководство по мемоизации в ReactuseState продвинутое использование в React
Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемXState - конечные автоматыТемизация в ReactТестирование хуковTailwind CSS с ReactSWR - библиотека для запросовStyled Components — стилизация через JSStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловПодключение Bootstrap к React-приложениюReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactMobX — реактивное управление состоянием в ReactМокирование APIМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
Transition API — плавные обновления интерфейса в ReactСерверные компоненты React (RSC) — подробный разбор и практикаStrictMode в React — как находить ошибки на этапе разработкиКак работает рендеринг в ReactReact Suspense — приостановка рендераЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать React DOM в проектеКак использовать элементы в ReactЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMCSS Modules в ReactFragment в React: группировка элементов без лишних узлов DOMConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий