YouTube outro
Ten-second end card with a next-video slot and a subscribe slot, sized for YouTube's end-screen elements.
YoutubeOutro, defaults
Installation
npx shadcn add https://www.reelcn.dev/r/youtube-outro.jsonFiles land in src/reelcn/ with the items it builds on. Dependencies: remotion, zod.
Register the composition
<Composition
id="YoutubeOutro"
component={YoutubeOutro}
schema={youtubeOutroSchema}
defaultProps={youtubeOutroDefaults}
calculateMetadata={youtubeOutroMetadata}
width={1920}
height={1080}
fps={30}
durationInFrames={1}
/>Render
npx remotion render YoutubeOutro # 16:9
npx remotion render YoutubeOutro --width=1080 --height=1920 # 9:16Props
PropTypeDescription
nextVideoTitle*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.
thumbnailstringThumbnail of the next video. A placeholder frame is drawn when omitted.
subscribestringChannel name for the subscribe slot.
Built from
Use
- The last seconds of a YouTube video
Avoid
- Openers. Use
youtube-intro
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/youtube-outro.tsx
/**
* @title YouTube Outro
* @category templates
* @description Ten-second end card with a next-video slot and a subscribe slot, sized for YouTube's end-screen elements.
* @duration data-driven
* @use The last seconds of a YouTube video
* @avoid Openers. Use `youtube-intro`
* @tags youtube, outro, end screen, subscribe, template
* @example
* <Composition
* id="YoutubeOutro"
* component={YoutubeOutro}
* schema={youtubeOutroSchema}
* defaultProps={youtubeOutroDefaults}
* calculateMetadata={youtubeOutroMetadata}
* width={1920}
* height={1080}
* fps={30}
* durationInFrames={1}
* />
*/
import { type CalculateMetadataFunction, Img } from "remotion";
import { z } from "zod";
import { BrandSolid } from "./brand-solid";
import { EndScreen } from "./end-screen";
import { STORY_FPS } from "./story";
import { TemplateFrame, templateSchema } from "./storyboard";
/** YouTube end screens run 5 to 20 seconds; ten leaves time to click. */
const OUTRO_SECONDS = 10;
export const YOUTUBE_OUTRO_FRAMES = OUTRO_SECONDS * STORY_FPS;
export const youtubeOutroSchema = templateSchema.extend({
nextVideoTitle: z.string(),
/** Thumbnail of the next video. A placeholder frame is drawn when omitted. */
thumbnail: z.string().optional(),
/** Channel name for the subscribe slot. */
subscribe: z.string().optional(),
});
export type YoutubeOutroProps = z.infer<typeof youtubeOutroSchema>;
export const youtubeOutroDefaults: YoutubeOutroProps = {
nextVideoTitle: "How we render ten thousand videos a day",
subscribe: "Northwind Labs",
};
export function YoutubeOutro({ nextVideoTitle, thumbnail, subscribe, ...fields }: YoutubeOutroProps) {
return (
<TemplateFrame fields={fields}>
<BrandSolid />
<EndScreen
nextTitle={nextVideoTitle}
channel={subscribe}
seconds={OUTRO_SECONDS - 2}
thumbnail={
thumbnail ? <Img src={thumbnail} style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : undefined
}
/>
</TemplateFrame>
);
}
export const youtubeOutroMetadata: CalculateMetadataFunction<YoutubeOutroProps> = () => ({
fps: STORY_FPS,
durationInFrames: YOUTUBE_OUTRO_FRAMES,
});