Changelog
Release video listing what is new, improved and fixed in a version, three changes per card set.
Changelog, defaults
Installation
npx shadcn add https://www.reelcn.dev/r/changelog.jsonFiles land in src/reelcn/ with the items it builds on. Dependencies: zod.
Register the composition
<Composition
id="Changelog"
component={Changelog}
schema={changelogSchema}
defaultProps={changelogDefaults}
calculateMetadata={changelogMetadata}
width={1920}
height={1080}
fps={30}
durationInFrames={1}
/>Render
npx remotion render Changelog # 16:9
npx remotion render Changelog --width=1080 --height=1920 # 9:16Props
PropTypeDescription
version*string
date*string
items*{ type: "new" | "improved" | "fixed"; text: string; }[]
theme"daylight" | "midnight" | "mono" | "ledger" | "afterglow" | "chromewave" | "neon" | "paper" | "sunset"Theme preset. Inherits the surrounding `ThemeProvider` when omitted.
brand{ accent?: string; logo?: string; font?: string; }
sfxbooleanPlays a soft whoosh as each scene starts.
musicstringBackground music file, looped under everything at 25% volume.
urlstring
Built from
Use
- Monthly or per-release update videos
- Turning a changelog page into something people watch
Avoid
- A whole product story. Use
product-launch
Make it with an agent
With the reelcn agent skill installed, ask for the video in plain words, such as "make a launch video for Relay with reelcn". The skill picks a template, sets its props, reviews a contact sheet and renders.
Guides
Components take their colors from a theme, their timing from motion props and their layout from the canvas.
Source
registry/items/changelog.tsx
/**
* @title Changelog
* @category templates
* @description Release video listing what is new, improved and fixed in a version, three changes per card set.
* @duration data-driven
* @use Monthly or per-release update videos
* @use Turning a changelog page into something people watch
* @avoid A whole product story. Use `product-launch`
* @tags changelog, release, updates, product, template
* @example
* <Composition
* id="Changelog"
* component={Changelog}
* schema={changelogSchema}
* defaultProps={changelogDefaults}
* calculateMetadata={changelogMetadata}
* width={1920}
* height={1080}
* fps={30}
* durationInFrames={1}
* />
*/
import { z } from "zod";
import type { Scene, Story } from "./story";
import { chunk, Storyboard, templateMetadata, templateSchema, templateStory } from "./storyboard";
const LABELS = { new: "New", improved: "Improved", fixed: "Fixed" } as const;
export const changelogSchema = templateSchema.extend({
version: z.string(),
date: z.string(),
items: z.array(z.object({ type: z.enum(["new", "improved", "fixed"]), text: z.string() })).min(1),
url: z.string().optional(),
});
export type ChangelogProps = z.infer<typeof changelogSchema>;
export const changelogDefaults: ChangelogProps = {
version: "2.4",
date: "September 2026",
items: [
{ type: "new", text: "Scheduled releases" },
{ type: "new", text: "A weekly digest in your team chat" },
{ type: "improved", text: "The editor opens twice as fast" },
{ type: "improved", text: "A calmer diff view" },
{ type: "fixed", text: "Links in footnotes open correctly" },
],
url: "relay.example.com/changelog",
};
export function changelogStory(props: ChangelogProps): Story {
const { version, date, items, url } = props;
return templateStory(props, [
{ type: "title", kicker: "Changelog", title: `Version ${version}`, subtitle: date, background: "grid" },
...chunk(items, 3).map(
(group): Scene => ({
type: "bullets",
items: group.map((item) => ({ text: item.text, badge: LABELS[item.type] })),
}),
),
{ type: "cta", title: "Available today", url },
]);
}
export function Changelog(props: ChangelogProps) {
return <Storyboard story={changelogStory(props)} />;
}
export const changelogMetadata = templateMetadata(changelogStory);