Democraft

Components and remocn

How visual components work in Democraft, and why remocn is the primary component library.

When you write scene.caption("Hello", { renderer: "remocn.kinetic-title" }), the renderer string selects which visual component renders that overlay. This page explains the component model, the remocn library, and how to use components in your scenes.

What is remocn?

Remocn = Remotion + shadcn. It is Democraft's primary component library for visual overlays — captions, callouts, titles, and other cinematic elements. Like shadcn, remocn components are copy-paste: you clone them into your project and you own the source.

Remocn is the recommended and primary source of components for Democraft overlays. The framework ships with built-in defaults, but for anything beyond plain text, remocn is where you turn.

Components are user-owned

Components do not live inside the @democraft/remotion package. They live in your project. You can:

  • Clone from the remocn registry (shadcn-style):

    pnpm dlx shadcn@latest add @remocn/soft-blur-in

    This copies the component source into your project. You read it, you modify it, you own it.

  • Write your own from scratch. Any React component that accepts the right props works.

In both cases, declare the component in the same demo.ts. Democraft generates the Remotion entry automatically:

demo.ts
import { defineDemo, defineVisual } from "@democraft/core";
import { BlurOutUp } from "./components/remocn/blur-out-up";
 
export default defineDemo({
  id: "launch",
  title: "Launch",
  source: { baseUrl: "http://localhost:3000" },
  visuals: {
    "local.launch-title": defineVisual(BlurOutUp),
  },
  async run({ demo }) {
    await demo.scene("intro", async (scene) => {
      await scene.visual(
        "local.launch-title",
        { text: "New analytics", speed: 1.2 },
        { duration: "1.5s" },
      );
    });
  },
});

TypeScript infers the visual ID and the component's props. Render with the same module: democraft render demo.ts -o demo.mp4.

The renderer string

Every caption and callout step accepts a renderer option — a string that names the visual component:

// Uses the default caption component (plain text pill)
await scene.caption("Welcome.");
 
// Uses the remocn kinetic-title component (animated blur-in)
await scene.caption("Create a workspace in seconds.", {
  renderer: "remocn.kinetic-title",
});
 
// Uses the remocn glass-callout component (frosted glass panel)
await scene.callout("project-card", {
  title: "Your project is ready",
  renderer: "remocn.glass-callout",
});

This string flows through the entire pipeline — authoring, compilation, capture, timeline resolution — untouched. Only at render time does the Remotion layer look up the string in a visual registry to find the matching React component.

Namespace convention

Renderer IDs follow <namespace>.<component>:

NamespaceMeaningExample
motion.*Framework defaults (used when renderer is omitted)motion.caption, motion.callout
remocn.*Components from the remocn libraryremocn.kinetic-title, remocn.glass-callout
local.*Your own project componentslocal.my-caption, local.pulse-callout

If you pass an unknown renderer or visual ID, Democraft reports an explicit error and lists the registered alternatives.

Built-in renderers

These ship with @democraft/remotion and are available without any custom entry:

Captions

RendererComponentVisual
motion.captionCaptionDark pill, bottom-centered, plain text. The default.
remocn.kinetic-titleKineticCaptionLarger bold text with per-character blur-in animation (via SoftBlurIn).

Callouts

RendererComponentVisual
motion.calloutCalloutDark box next to target, teal title. The default.
remocn.glass-calloutGlassCalloutLight frosted-glass box, blue title.

Using components in a scene

Add a caption or callout step to your scene with a renderer option:

await demo.scene("intro", async (scene) => {
  await scene.goto("/dashboard");
  await scene.establish("dashboard");
  await scene.caption("Create a workspace in seconds.", {
    renderer: "remocn.kinetic-title",
  });
  await scene.hold("5000ms");
});

Built-ins and components declared in demo.ts work without another file. A custom entry is only needed when replacing the whole composition or other advanced Remotion behavior.

Next steps

On This Page

On this page