og-image-generator
Generate and optimize Open Graph meta images for social media sharing. Use this skill when building web applications that need dynamic OG image generation with support for Vercel's @vercel/og library, pre-generated image storage, and social media optimization (Twitter Cards, Facebook, LinkedIn). Handles dynamic routes, performance optimization, and includes best practices for crawler compatibility and testing.
Best use case
og-image-generator is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Generate and optimize Open Graph meta images for social media sharing. Use this skill when building web applications that need dynamic OG image generation with support for Vercel's @vercel/og library, pre-generated image storage, and social media optimization (Twitter Cards, Facebook, LinkedIn). Handles dynamic routes, performance optimization, and includes best practices for crawler compatibility and testing.
Teams using og-image-generator should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/og-image-generator/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How og-image-generator Compares
| Feature / Agent | og-image-generator | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Generate and optimize Open Graph meta images for social media sharing. Use this skill when building web applications that need dynamic OG image generation with support for Vercel's @vercel/og library, pre-generated image storage, and social media optimization (Twitter Cards, Facebook, LinkedIn). Handles dynamic routes, performance optimization, and includes best practices for crawler compatibility and testing.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Open Graph Image Generator
Generate high-performance Open Graph images for social media sharing with support for dynamic generation and pre-generated storage patterns.
## When to use
Use this skill when:
- Building a web application that needs OG images for social sharing
- Creating meta images for articles, blog posts, or user-generated content
- Optimizing social media previews for Twitter Cards, Facebook, or LinkedIn
- Need both dynamic generation (few pages) and pre-generation (many pages) patterns
- Implementing performance-critical image generation with crawler timeout awareness
## Quick Start: Dynamic Generation (3 minutes)
### 1. Install dependency
```bash
npm install @vercel/og
```
### 2. Create API route (Next.js)
```typescript
import { ImageResponse } from '@vercel/og';
export async function GET(request: Request) {
return new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
backgroundColor: '#0a0f1c',
padding: '100px',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}>
<h1 style={{ color: '#fff', fontSize: '64px', fontWeight: 'bold' }}>
Your Title
</h1>
</div>
),
{ width: 2400, height: 1200 } // 2x scale for retina
);
}
```
### 3. Add meta tags
```html
<meta property="og:image" content="https://yoursite.com/api/og" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="600" />
<meta name="twitter:card" content="summary_large_image" />
```
### 4. Test
Visit: https://cards-dev.twitter.com/validator and paste your URL.
---
## Production Pattern: Pre-Generated Images
For user-generated content or high-traffic pages, pre-generate images when content is created to ensure instant crawler responses.
### Pre-generation at content creation
```typescript
import { ImageResponse } from '@vercel/og';
async function generateAndStoreOgImage(contentData) {
const image = new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: '#0a0f1c',
padding: '100px',
justifyContent: 'space-between',
}}>
<h1 style={{ color: '#fff', fontSize: '64px', fontWeight: 'bold' }}>
{contentData.title}
</h1>
<p style={{ color: '#b0b0b0', fontSize: '28px' }}>
{contentData.description}
</p>
</div>
),
{ width: 2400, height: 1200 }
);
const buffer = await image.arrayBuffer();
// Store in database
await db.ogImages.insert({
contentId: contentData.id,
imageData: buffer,
mimeType: 'image/png',
});
return buffer;
}
```
### Serve pre-generated image
```typescript
export async function getOgImage(contentId: string) {
const { imageData } = await db.ogImages.findOne({ contentId });
return new Response(imageData, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=86400', // 24 hours
},
});
}
```
### Express pattern
```typescript
app.get('/og/:slug.png', async (req, res) => {
try {
const { slug } = req.params;
const content = await db.getContent(slug);
const image = new ImageResponse(
(/* JSX here */),
{ width: 2400, height: 1200 }
);
const buffer = await image.arrayBuffer();
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'public, max-age=86400');
res.send(Buffer.from(buffer));
} catch (error) {
res.status(500).send('Generation failed');
}
});
```
---
## Decision: Dynamic vs Pre-Generated
**Use Dynamic Generation if:**
- Few unique pages (<50)
- Content rarely changes
- Generation time < 2 seconds
- Traffic is low to medium
**Use Pre-Generation if:**
- Many unique pages (100+)
- User-generated content
- High traffic expected
- Need instant crawler response (avoid timeouts)
**Recommended:** Pre-generate for production; dynamic for development/testing.
---
## HTML Meta Tags
Include these in your page `<head>`:
```html
<!-- Open Graph -->
<meta property="og:image" content="https://example.com/og/page.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="600" />
<meta property="og:image:type" content="image/png" />
<meta property="og:title" content="Your Title" /> <!-- Zero-width space required -->
<meta property="og:description" content="Brief description" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://example.com/og/page.png" />
<meta name="twitter:title" content="Your Title" />
```
**Important:** Include a zero-width space () in `og:title` to prevent crawlers from using default title when og:image is present.
---
## Design Best Practices
### Dimensions
- **Size:** 1200×600px (2:1 aspect ratio)
- **Rendering:** 2400×1200px (2x scale for retina quality)
- **Safe margins:** 50-56px on all sides
- **Format:** PNG (lossless, predictable)
### Visual Design
- **High contrast:** Text readable at small thumbnail sizes
- **Minimal text:** 3-5 words maximum for headline
- **Brand colors:** Consistent palette with your site
- **No complex gradients:** Use solid colors or simple patterns
### Performance
- **Generation time:** Target <3 seconds (crawlers timeout at ~5s)
- **File size:** Keep <200KB per image
- **Cache:** Set to 24 hours minimum (`Cache-Control: public, max-age=86400`)
---
## Troubleshooting
| Issue | Solution |
|-------|----------|
| Twitter shows blank preview | Check: zero-width space in og:title, image is 1200×600, URL is public |
| Crawler timeout (X/Twitter) | Switch to pre-generated images; dynamic generation is too slow |
| Image doesn't load on social media | Verify CDN/storage URL is public; check CORS if cross-origin |
| Generation takes >5 seconds | Reduce complexity; pre-generate instead; use smaller images |
| File size >500KB | Reduce color palette; simplify shapes; avoid complex gradients |
| Meta tags not appearing | Ensure tags are in `<head>` (not `<body>`); use valid HTML |
---
## Validation Checklist
- [ ] Image dimensions: 1200×600px (or 2400×1200px for 2x)
- [ ] Aspect ratio: 2:1
- [ ] Safe margins: 50-56px padding
- [ ] High contrast colors (WCAG AA minimum)
- [ ] File format: PNG
- [ ] File size: <200KB
- [ ] Zero-width space in `og:title`
- [ ] `Cache-Control` header: `public, max-age=86400`
- [ ] Test with Twitter Card Validator: https://cards-dev.twitter.com/validator
- [ ] Response time <3 seconds (dynamic) or instant (pre-generated)
- [ ] Fallback error handling if generation fails
---
## Implementation Patterns
### Next.js with Dynamic Route
```typescript
// app/api/og/[slug]/route.ts
import { ImageResponse } from '@vercel/og';
export async function GET(
request: Request,
{ params }: { params: { slug: string } }
) {
const content = await getContentBySlug(params.slug);
return new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
backgroundColor: '#0a0f1c',
padding: '100px',
}}>
<h1 style={{ color: '#fff', fontSize: '64px' }}>
{content.title}
</h1>
</div>
),
{ width: 2400, height: 1200 }
);
}
```
### Error Handling Pattern
```typescript
try {
const image = new ImageResponse(/* ... */);
return new Response(await image.arrayBuffer(), {
headers: { 'Content-Type': 'image/png' },
});
} catch (error) {
console.error('OG generation failed:', error);
// Return fallback or placeholder
return new Response('Image generation failed', { status: 500 });
}
```
### Hybrid Cache Pattern
```typescript
export async function getOgImage(contentId: string) {
// 1. Check cache
const cached = await cache.get(`og:${contentId}`);
if (cached) return cached;
// 2. Check if generating
if (isGenerating.has(contentId)) {
await waitFor(contentId);
return (await cache.get(`og:${contentId}`));
}
// 3. Generate with timeout
isGenerating.add(contentId);
try {
const buffer = await Promise.race([
generateOgImage(contentId),
timeout(4000), // Crawler safety
]);
await cache.set(`og:${contentId}`, buffer);
return buffer;
} finally {
isGenerating.delete(contentId);
}
}
```
---
## Common Variants
### Minimal Design
```typescript
const image = new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#0a0f1c',
padding: '100px',
}}>
<h1 style={{ color: '#fff', fontSize: '72px', textAlign: 'center' }}>
{title}
</h1>
</div>
),
{ width: 2400, height: 1200 }
);
```
### Card Style (Articles)
```typescript
const image = new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
backgroundColor: '#0a0f1c',
padding: '100px',
}}>
<div style={{ color: '#00d9ff', fontSize: '24px', fontWeight: '600' }}>
{category}
</div>
<h1 style={{ color: '#fff', fontSize: '64px', fontWeight: 'bold' }}>
{title}
</h1>
<div style={{ color: '#808080', fontSize: '20px' }}>
{publishDate}
</div>
</div>
),
{ width: 2400, height: 1200 }
);
```
### With Image
```typescript
const image = new ImageResponse(
(
<div style={{ display: 'flex' }}>
<img
src={imageUrl}
style={{
width: '600px',
height: '1200px',
objectFit: 'cover',
}}
alt="Preview"
/>
<div style={{ flex: 1, padding: '100px' }}>
<h1 style={{ color: '#fff', fontSize: '64px' }}>{title}</h1>
</div>
</div>
),
{ width: 2400, height: 1200 }
);
```
---
## Resources & Links
- **Vercel OG Documentation:** https://vercel.com/docs/og-image-generation
- **Twitter Card Validator:** https://cards-dev.twitter.com/validator
- **Open Graph Protocol:** https://ogp.me
- **Twitter Cards Docs:** https://developer.twitter.com/en/docs/twitter-for-websites/cards
---
## Summary
1. **For quick setup:** Use dynamic generation pattern above
2. **For production:** Pre-generate images at content creation
3. **For testing:** Use Twitter Card Validator
4. **For performance:** Monitor generation time; cache aggressively
5. **For crawlers:** Always be aware of 5-second timeout; pre-generation is safer
---
## Version
**v1.0** | MIT License | Production ReadyRelated Skills
generator
Générateur de Skill - Crée de nouveaux fichiers SKILL.md depuis les définitions YAML d'agents
EchoKit Config Generator
Generate config.toml for EchoKit servers with interactive setup for ASR, TTS, LLM services, MCP servers, API key entry, and server launch
bigconfig-generator
Use this skill when creating or updating Bigeye monitoring configurations (bigconfig.yml files) for BigQuery tables. Works with metadata-manager skill.
all-images-ai-automation
Automate All Images AI tasks via Rube MCP (Composio). Always search tools first for current schemas.
ai-image-generator
使用 ModelScope 等平台生成 AI 图像。当用户需要生成图像、设计图标、创建角色立绘,或需要帮助编写 AI 绘画提示词时使用此技能。支持直接生成图像和仅优化提示词两种模式。
xhs-images
Xiaohongshu (Little Red Book) infographic series generator with multiple style options. Breaks down content into 1-10 cartoon-style infographics. Use when user asks to create "小红书图片", "XHS images", or "RedNote infographics".
x-image-cards
Create X/Twitter cards that look like images, not marketing banners. Use when asked to "create OG images", "set up X cards", "make social cards", or "twitter card without text".
wiro-image-fill
Generate missing or placeholder images in a project by calling the Wiro image generation API, saving assets under public/assets generated folders, and producing a JSON mapping. Use when you see empty img src, placeholder.png, or other image gaps that need real assets.
thumbnail-generator
Generate prompts for dev.to blog thumbnail/cover images in hand-drawn infographic style. Use when creating cover images, thumbnails, or featured images for blog posts. Recommended size 1000x420 pixels.
seedream-image-generator
Generate images using the Doubao SeeDream API based on text prompts. Use this skill when users request AI-generated images, artwork, illustrations, or visual content creation. The skill handles API calls, downloads generated images to the project's /pic folder, and supports batch generation of up to 4 sequential images.
placeholder-images
Rule to use placekitten.com for placeholder images in seed data.
nanobanana-image
Nano Banana (Google Gemini API) を使って画像を生成・編集するスキル。「画像を生成して」「イラストを作って」「○○の絵を描いて」「画像を作成」「この画像を編集して」「この画像をもとに○○を作って」「generate an image」「create a picture」「edit this image」などの依頼があった場合に使用。テキストからの生成、参照画像からの生成、画像編集、Google検索グラウンディングによる最新情報を反映した画像生成に対応。「最新の○○」「トレンドを反映」「リアルタイム情報」といった依頼にも対応可能。