Files
json-render/packages/remotion/README.md
Chris Tate c1b1aaaddb custom schema system (#56)
* add rate limits

* fix lint

* better

generate prompt

update examples

* fixes

* fixes

* new api

* update docs

* fixes

* fixes

* generated prompt

* Add tests for catalog validation and prompt generation

- Test generateSystemPrompt with components, actions, custom rules
- Test new defineCatalog API from schema system
- Test catalog.prompt() method with custom rules
- Test catalog.validate() for valid and invalid specs
- Test catalog.zodSchema() for custom validation
- Test catalog.jsonSchema() for structured outputs
- Add tests for nested specs with children
- Add tests for rejecting invalid component types

* Fix lint: pass children as nested elements in renderer

Change from children={...} prop to nested children pattern
to satisfy react/no-children-prop rule.

* Fix lint errors in dashboard and web apps

- Disable react/prop-types in both eslint configs (TypeScript handles this)
- Allow styled-jsx 'jsx' property in dashboard
- Add DATABASE_URL to turbo env allowlist
- Remove unused drizzle-orm imports (and, sql)
- Suppress unused variable warnings where intentional

* Add server entry point for @json-render/remotion

The main package entry includes React components that require
client-side context (React.createContext). This causes build
failures when importing in server-side API routes.

Added `@json-render/remotion/server` entry point that exports
only schema and catalog definitions without React dependencies:
- schema, RemotionSchema, RemotionSpec
- standardComponentDefinitions, standardTransitionDefinitions
- standardEffectDefinitions
- Type exports for catalogs

Updated remotion example to import from /server in catalog.ts

* Make dashboard database connection lazy-initialized

The database connection threw an error at module load time if
DATABASE_URL was not set, causing builds to fail in CI where
the database is not available.

Changed to lazy initialization using a Proxy so the error only
occurs when the database is actually used at runtime, not at
build time.

* Fix previousSpec property name and lazy rate limiting

- Fix property name mismatch: playground now passes previousSpec
  instead of previousTree to match what useUIStream hook expects
- Make rate limiting lazy-initialized to avoid runtime errors when
  Redis env vars (KV_REST_API_URL, KV_REST_API_TOKEN) are not set
- Rate limiting gracefully becomes a no-op when Redis is unavailable
2026-02-05 03:59:56 -06:00

6.8 KiB

@json-render/remotion

Remotion video renderer for json-render. Turn JSON timeline specs into video compositions.

Installation

npm install @json-render/remotion @json-render/core remotion @remotion/player zod

Quick Start

1. Create a Catalog

import { defineCatalog } from "@json-render/core";
import {
  schema,
  standardComponentDefinitions,
  standardTransitionDefinitions,
  standardEffectDefinitions,
} from "@json-render/remotion";

// Use standard definitions or add your own
export const videoCatalog = defineCatalog(schema, {
  components: {
    ...standardComponentDefinitions,
    // Add custom components here
  },
  transitions: standardTransitionDefinitions,
  effects: standardEffectDefinitions,
});

2. Render with the Player

import { Player } from "@remotion/player";
import { Renderer } from "@json-render/remotion";

function VideoPlayer({ spec }) {
  return (
    <Player
      component={Renderer}
      inputProps={{ spec }}
      durationInFrames={spec.composition.durationInFrames}
      fps={spec.composition.fps}
      compositionWidth={spec.composition.width}
      compositionHeight={spec.composition.height}
      controls
    />
  );
}

Timeline Spec Format

interface TimelineSpec {
  composition: {
    id: string;
    fps: number;
    width: number;
    height: number;
    durationInFrames: number;
  };
  tracks: Array<{
    id: string;
    name: string;
    type: "video" | "overlay" | "audio";
    enabled: boolean;
  }>;
  clips: Array<{
    id: string;
    trackId: string;
    component: string;         // Component name from catalog
    props: object;             // Component-specific props
    from: number;              // Start frame
    durationInFrames: number;
    transitionIn?: {
      type: string;
      durationInFrames: number;
    };
    transitionOut?: {
      type: string;
      durationInFrames: number;
    };
  }>;
  audio: {
    tracks: Array<{
      id: string;
      src: string;
      from: number;
      durationInFrames: number;
      volume?: number;
    }>;
  };
}

Example spec:

{
  "composition": {
    "id": "intro-video",
    "fps": 30,
    "width": 1920,
    "height": 1080,
    "durationInFrames": 300
  },
  "tracks": [
    { "id": "main", "name": "Main Video", "type": "video", "enabled": true },
    { "id": "overlay", "name": "Overlays", "type": "overlay", "enabled": true }
  ],
  "clips": [
    {
      "id": "clip-1",
      "trackId": "main",
      "component": "TitleCard",
      "props": {
        "title": "Welcome",
        "subtitle": "To the future",
        "backgroundColor": "#1a1a1a",
        "textColor": "#ffffff"
      },
      "from": 0,
      "durationInFrames": 90,
      "transitionIn": { "type": "fade", "durationInFrames": 15 },
      "transitionOut": { "type": "fade", "durationInFrames": 15 }
    }
  ],
  "audio": { "tracks": [] }
}

Standard Components

The package includes pre-built video components:

Component Description Key Props
TitleCard Full-screen title with subtitle title, subtitle, backgroundColor, textColor
ImageSlide Full-screen image display src, alt, fit
SplitScreen Two-column layout leftTitle, rightTitle, leftContent, rightContent
QuoteCard Quote with attribution quote, author, role
StatCard Large statistic display value, label, trend
LowerThird Name/title overlay name, title
TextOverlay Centered text text, fontSize, fontFamily
TypingText Terminal typing effect text, charsPerSecond, showCursor
LogoBug Corner logo overlay src, position, size
VideoClip Video playback src

Standard Transitions

Transition Description
fade Opacity fade in/out
slideLeft Slide from right
slideRight Slide from left
slideUp Slide from bottom
slideDown Slide from top
zoom Scale zoom in/out
wipe Horizontal wipe

Custom Components

Add custom components to the Renderer:

import { Renderer, standardComponents, ClipWrapper } from "@json-render/remotion";
import type { Clip } from "@json-render/remotion";

// Define a custom component
function CustomOverlay({ clip }: { clip: Clip }) {
  return (
    <ClipWrapper clip={clip}>
      <div style={{ backgroundColor: clip.props.color }}>
        {clip.props.message}
      </div>
    </ClipWrapper>
  );
}

// Merge with standard components
const customComponents = {
  ...standardComponents,
  CustomOverlay,
};

// Pass to Renderer
<Player
  component={Renderer}
  inputProps={{ spec, components: customComponents }}
  // ...
/>

Hooks and Utilities

useTransition

Calculate transition styles for a clip:

import { useTransition } from "@json-render/remotion";
import { useCurrentFrame } from "remotion";

function MyComponent({ clip }: { clip: Clip }) {
  const frame = useCurrentFrame();
  const transition = useTransition(clip, frame);

  return (
    <div style={{ opacity: transition.opacity }}>
      Content
    </div>
  );
}

ClipWrapper

Apply transitions automatically:

import { ClipWrapper } from "@json-render/remotion";

function MyComponent({ clip }: { clip: Clip }) {
  return (
    <ClipWrapper clip={clip}>
      {/* Content gets transition styles applied */}
      <div>My content</div>
    </ClipWrapper>
  );
}

Generate AI Prompts

const systemPrompt = videoCatalog.prompt();
// Returns detailed prompt with:
// - Component descriptions and props
// - Transition types
// - Effect definitions
// - Timeline spec format requirements

Exports

Components

import {
  // Main renderer
  Renderer,
  standardComponents,

  // Standard components
  TitleCard,
  ImageSlide,
  SplitScreen,
  QuoteCard,
  StatCard,
  LowerThird,
  TextOverlay,
  TypingText,
  LogoBug,
  VideoClip,

  // Utilities
  ClipWrapper,
  useTransition,
} from "@json-render/remotion";

Types

import type {
  Clip,
  TimelineSpec,
  AudioTrack,
  TransitionStyles,
  ClipComponent,
  ComponentRegistry,
} from "@json-render/remotion";

Schema and Catalog Definitions

import {
  schema,
  standardComponentDefinitions,
  standardTransitionDefinitions,
  standardEffectDefinitions,
} from "@json-render/remotion";

Why Different from React?

Feature @json-render/react @json-render/remotion
Spec Format Element tree (nested components) Timeline (tracks + clips)
Components UI components (Button, Card) Video components (scenes, overlays)
Timing User interactions Frame-based animations
Output React DOM Remotion video

json-render is "JSON to render" - the spec format and components are completely flexible per renderer.