reelcn
GitHubBack to landing

YouTube outro

Ten-second end card with a next-video slot and a subscribe slot, sized for YouTube's end-screen elements.

YoutubeOutro, defaults
YoutubeOutro, defaults thumbnail, 16:9

Installation

npx shadcn add https://www.reelcn.dev/r/youtube-outro.json

Files 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:16

Props

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

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,
});

On this page