Templates

Templates are React components that define your images and videos. They use Tailwind CSS for styling and export metadata that loopwind uses for rendering.

Installing Templates

Official Templates

loopwind add image-template
loopwind add video-template

Templates are installed to .loopwind/<template-name>/.

Direct URLs

loopwind add https://example.com/templates/my-template.json

Local Filesystem

loopwind add ./my-templates/banner-hero
loopwind add /Users/you/templates/social-card

Image Templates

Basic Structure

// .loopwind/banner-hero/template.tsx
export const meta = {
  name: "banner-hero",
  type: "image",
  description: "Hero banner with gradient background",
  size: { width: 1600, height: 900 },
  props: { title: "string", subtitle: "string" }
};

export default function BannerHero({ title, subtitle, tw }) {
  return (
    <div style={tw('flex flex-col justify-center items-center w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
      <h1 style={tw('text-7xl font-bold text-white mb-4')}>
        {title}
      </h1>
      <p style={tw('text-2xl text-white/80')}>
        {subtitle}
      </p>
    </div>
  );
}

Rendering Images

# Render with inline props
loopwind render banner-hero '{"title":"Hello World","subtitle":"Welcome"}'

# Custom output name
loopwind render banner-hero '{"title":"Hello"}' --out custom-name.png

# Different format
loopwind render banner-hero '{"title":"Hello"}' --format jpeg --quality 95

# Use a props file
loopwind render banner-hero props.json

Output Formats

FormatBest For
PNG (default)Transparency, sharp text, logos
JPEGPhotographs, gradients, smaller files
SVGVector graphics, scalable designs

Video Templates

Basic Structure

// .loopwind/video-intro/template.tsx
export const meta = {
  name: "video-intro",
  type: "video",
  description: "Animated intro with bounce-in title",
  size: { width: 1920, height: 1080 },
  video: { fps: 30, duration: 3 },
  props: { title: "string" }
};

export default function VideoIntro({ tw, title }) {
  return (
    <div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
      <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/600')}>
        {title}
      </h1>
    </div>
  );
}

Rendering Videos

# Render with inline props
loopwind render video-intro '{"title":"Welcome!"}' --out intro.mp4

# Faster encoding with FFmpeg
loopwind render video-intro '{"title":"Welcome!"}' --ffmpeg

# Higher quality (lower CRF = better)
loopwind render video-intro '{"title":"Welcome!"}' --crf 18

FPS and Duration

video: { fps: 30, duration: 3 }  // 90 frames total
FPSUse Case
24Cinematic look, smaller files
30Standard web video
60Smooth animations

Video-Specific Props

Templates receive these additional props:

  • frame - Current frame number (0 to totalFrames - 1)
  • progress - Animation progress from 0 to 1
export default function MyVideo({ frame, progress }) {
  // frame: 0, 1, 2, ... 89 (for 3s @ 30fps)
  // progress: 0.0 at start, 0.5 at middle, 1.0 at end
}

Encoding Options

EncoderCommandUse Case
WASM (default)loopwind render ...CI/CD, no dependencies
FFmpegloopwind render ... --ffmpegFaster, smaller files

Install FFmpeg: brew install ffmpeg (macOS)


Animation Classes

Use Tailwind-style animation classes for videos:

// Enter animations: enter-{type}/{delay}/{duration}
<h1 style={tw('enter-fade-in/0/500')}>Fade in at start</h1>
<h1 style={tw('enter-bounce-in-up/300/400')}>Bounce in after 300ms</h1>

// Exit animations: exit-{type}/{start}/{duration}
<div style={tw('exit-fade-out/2500/500')}>Fade out at 2.5s</div>

// Loop animations: loop-{type}/{duration}
<div style={tw('loop-float/1000')}>Continuous floating</div>
<div style={tw('loop-spin/1000')}>Spinning</div>

// Easing
<h1 style={tw('ease-out enter-slide-left/0/500')}>Smooth slide</h1>

See the full Animation documentation for all classes.


Common Sizes

Social Media

  • Twitter/X Card: 1200x675
  • Facebook/OG: 1200x630
  • Instagram Post: 1080x1080
  • LinkedIn Post: 1200x627

Web Graphics

  • Hero Banner: 1920x1080
  • Blog Header: 1600x900
  • Thumbnail: 640x360

Example Templates

Open Graph Image

export const meta = {
  name: "og-image",
  type: "image",
  size: { width: 1200, height: 630 },
  props: { title: "string", description: "string" }
};

export default function OGImage({ tw, image, title, description }) {
  return (
    <div style={tw('flex w-full h-full bg-white')}>
      <div style={tw('flex-1 flex flex-col justify-between p-12')}>
        <img src={image('logo.svg')} style={tw('h-12 w-auto')} />
        <div>
          <h1 style={tw('text-5xl font-bold text-gray-900 mb-4')}>{title}</h1>
          <p style={tw('text-xl text-gray-600')}>{description}</p>
        </div>
        <p style={tw('text-gray-400')}>yoursite.com</p>
      </div>
    </div>
  );
}

Animated Intro

export const meta = {
  name: "animated-intro",
  type: "video",
  size: { width: 1920, height: 1080 },
  video: { fps: 60, duration: 3 },
  props: { title: "string", subtitle: "string" }
};

export default function AnimatedIntro({ tw, title, subtitle }) {
  return (
    <div style={tw('flex flex-col items-center justify-center w-full h-full bg-background')}>
      <h1 style={tw('text-8xl font-bold text-foreground ease-out enter-bounce-in-up/0/400')}>
        {title}
      </h1>
      <p style={tw('text-2xl text-muted-foreground mt-4 ease-out enter-fade-in-up/300/400')}>
        {subtitle}
      </p>
    </div>
  );
}

Next Steps