Beyond the Built-ins: A Guide to Custom Layer Types in VideoFlow
September 1, 2026 · By VideoFlowUnlock the full potential of VideoFlow by building custom layer types. Learn how to extend the renderer to handle specialized visuals, data-driven charts, and more.
Beyond the Built-ins: A Guide to Custom Layer Types in VideoFlow
VideoFlow ships with a robust set of built-in primitives—text, images, video, audio, captions, and shapes. For the vast majority of use cases, these are all you need to build stunning, code-driven videos. But what happens when you need to render something specialized? Perhaps a real-time data visualization, a dynamic QR code, or a complex generative background that doesn't fit into a standard image or video container?
This is where VideoFlow’s extension system shines. Unlike rigid video frameworks that lock you into a fixed set of components, VideoFlow is designed to be extended from the ground up. Because the core builder API produces a portable VideoJSON document, and the renderers use a flexible registry-based architecture, you can define and register your own Custom Layer Types that render identically across the browser, the server, and the live preview.
In this guide, we’ll walk through the architecture of a custom layer and show you how to plug your own logic into the official VideoFlow renderers.

The Architecture of a Layer
To create a custom layer, you need to understand the two-part nature of VideoFlow layers. This separation of concerns is what allows VideoFlow to be so portable:
- The Builder Layer (Core): This is the class you interact with in your TypeScript or JavaScript script (e.g., when you call
$.addText). Its job is to handle the fluent API, manage timing, and compile your intent into the VideoJSON schema. - The Runtime Layer (Renderer): This is the class that lives inside the renderer (like
@videoflow/renderer-browser). It receives the compiled JSON and is responsible for producing the actual visual output—usually by creating DOM elements or drawing to a canvas—and updating them frame-by-frame as the timeline progresses.
To build a custom extension, we primarily focus on the Runtime Layer. Every runtime layer in VideoFlow extends a base class called RuntimeBaseLayer. This base class provides the "heavy lifting" for timing math, keyframe interpolation, and property application, so you can focus on the unique rendering logic of your layer.
Step 1: Define Your Runtime Layer
Let’s say we want to create a QRCodeLayer. We want it to take a value property and render a QR code using a library like qrcode. This is a perfect example of a layer that would be difficult to represent using just images, especially if the QR code needs to change dynamically based on user data.
import { RuntimeBaseLayer } from '@videoflow/renderer-browser';
import QRCode from 'qrcode';
export class RuntimeQRCodeLayer extends RuntimeBaseLayer {
private canvas: HTMLCanvasElement | null = null;
// 1. Generate the initial element for the DOM
override generateElement() {
this.canvas = document.createElement('canvas');
this.$element = document.createElement('div');
this.$element.appendChild(this.canvas);
// We set some default styles to ensure it behaves like a standard layer
this.$element.style.display = 'flex';
this.$element.style.justifyContent = 'center';
this.$element.style.alignItems = 'center';
return this.$element;
}
// 2. React to property changes every single frame
override async renderFrame(frame: number) {
// RuntimeBaseLayer.calculateProperties handles all keyframe interpolation for us!
const props = this.calculateProperties(frame);
if (this.canvas && props.value) {
// Render the QR code to the canvas
await QRCode.toCanvas(this.canvas, props.value, {
width: props.size * (this.projectWidth / 100), // convert our 'em' units to pixels
margin: 0,
color: { dark: props.color || '#000000', light: '#00000000' }
});
}
// Apply standard visual properties like opacity, position, and rotation
this.applyProperties(props);
}
}
Step 2: Register the Layer Type with the Renderer
Once you have your runtime class defined, you need to tell the renderer how to handle the new type. VideoFlow uses a per-instance LayerTypeRegistry. This is a deliberate architectural choice: it allows you to have different layer implementations in the same application without global collisions—which is exactly how the VideoFlow Playground manages different versions of the engine.
import { BrowserRenderer } from '@videoflow/renderer-browser';
const renderer = new BrowserRenderer(videoJSON);
// Register our new 'qrcode' type
renderer.registerLayerType('qrcode', {
runtime: RuntimeQRCodeLayer,
propertiesDefinition: {
value: { type: 'string', default: '' },
size: { type: 'number', default: 10, unit: 'em' },
color: { type: 'string', default: '#ffffff' },
// We inherit standard visual props like position and opacity from the base
...RuntimeBaseLayer.propertiesDefinition,
},
});
await renderer.render();

Why This Matters for Engineering Teams
If you are building a SaaS dashboard with automated video recaps, you might need to render complex charts or personalized data visualizations that match your app's brand perfectly. Instead of trying to hack together hundreds of static addImage calls or dealing with fragile SVG exports, you can build a single ChartLayer that talks directly to your existing D3 or Chart.js logic.
This approach has three major benefits for production pipelines:
- Performance: Custom layers can use specialized rendering paths (like Canvas 2D or WebGL) that are significantly faster than maintaining heavy DOM trees for complex visuals.
- Maintainability: Your video logic stays clean and declarative. Your builder script just says
$.addLayer({ type: 'chart', data: [...] })instead of managing low-level rendering code. - Portability: Because the renderer handles the registration, the same VideoJSON can be sent to a
ServerRendererin a serverless function for batch processing, or aDomRendererfor a frame-accurate live preview in your React application.
Taking it Further
Custom layers are just the beginning of how you can extend the toolkit. You can also define custom GLSL effects and transition presets to build a truly unique visual language for your automated content. The goal of VideoFlow is to provide the strongest possible foundation for programmatic video, while staying out of your way when you need to innovate.
Ready to build your first extension? Check out the source code on GitHub to see how the built-in layers are implemented, or dive into the API Reference for a deep dive into the RuntimeBaseLayer lifecycle.
Happy rendering!