skillmake
← marketplace
creatorstoolsha:793377efcc726c96manual

og-image-generator

Use when shipping per-post social cards across an entire creator site — Satori-driven JSX-to-SVG-to-PNG pipeline with brand-consistent templates and dynamic per-post variables (title, author, date, tag).

Tutorials · creator-attached
One-line install
curl --create-dirs -fsSL https://skillmake.xyz/i/og-image-generator -o ~/.claude/skills/og-image-generator/SKILL.md

The hash above pins this exact content. The file we serve at /api/marketplace/og-image-generator-793377ef/raw always matches sha:793377efcc726c96.

3,650 chars · ~913 tokens
---
name: og-image-generator
description: Use when shipping per-post social cards across an entire creator site — Satori-driven JSX-to-SVG-to-PNG pipeline with brand-consistent templates and dynamic per-post variables (title, author, date, tag).
source: https://github.com/vercel/satori
generated: 2026-05-07T21:42:35.222Z
category: tool
audience: creators
---

## Tutorials

- https://skillmake.xyz/v/og-image-generator.mp4

## When to use

- Generating Open Graph cards for every blog post / video / project on a creator site
- Replacing manually-designed Figma-export-per-post with a single template
- Producing 1200×630 PNG cards on demand at the edge with no wkhtmltoimage / Puppeteer dependency
- Driving cards from MDX frontmatter or a CMS without designer involvement

## Key concepts

### Satori

Vercel's library that renders JSX → SVG using a subset of CSS (flex layout, fonts, basic transforms). Output SVG is then rasterised to PNG via @resvg/resvg-js or sharp. Runs in the edge runtime, ~5ms render times.

### branded template

One JSX function per layout variant — hero, default, talk, podcast. Accepts {title, subtitle, image, accent} and returns the same composition every time. Makes the whole site visually coherent across hundreds of posts.

### font loading

Satori needs the font as an ArrayBuffer — not a URL. Fetch the .ttf/.woff from Google Fonts at build (or store in /public) and pass to satori({fonts: [{name, data, weight, style}]}). Variable fonts not yet supported.

## API reference

```
Next.js opengraph-image.tsx route
```

App Router convention: a file named opengraph-image.tsx exports a default async function returning JSX. Next + Satori auto-render it as the OG card for the parent route.

```
// app/posts/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';
export default async function Image({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return new ImageResponse(
    (
      <div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%', background: '#0b0d10', color: '#e8ecf1', padding: 80 }}>
        <div style={{ fontSize: 56, fontWeight: 700 }}>{post.title}</div>
        <div style={{ marginTop: 'auto', fontSize: 28, color: '#a8ff60' }}>{post.author} · {post.date}</div>
      </div>
    ),
    { ...size }
  );
}
```

```
Satori standalone (non-Next)
```

For Astro / Hono / static-site builds — render JSX with satori() then rasterise with @resvg/resvg-js.

```
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
import fs from 'node:fs/promises';

const font = await fs.readFile('./fonts/Inter-Bold.ttf');
const svg = await satori(<Card title={post.title} />, {
  width: 1200, height: 630,
  fonts: [{ name: 'Inter', data: font, weight: 700, style: 'normal' }],
});
const png = new Resvg(svg).render().asPng();
await fs.writeFile(`./public/og/${post.slug}.png`, png);
```

## Gotchas

- Satori supports only a subset of CSS — no grid, no calc(), no transitions. Stick to flex.
- Variable fonts are not supported; load each weight/style as a separate static font file.
- External images need to be fetched and inlined as base64 unless your runtime supports remote URLs (Next /og handles this; standalone needs manual inlining).
- Don't generate cards on every request without caching — Satori is fast but the rasterise step is CPU-bound. Cache by content hash.

---
Generated by SkillMake from https://github.com/vercel/satori on 2026-05-07T21:42:35.222Z.
Verify against source before relying on details.

File: ~/.claude/skills/og-image-generator/SKILL.md