Social card generator template
- Node.js
- Express
Make 1200×630 Open Graph images from a title, description and your brand colors.
A Node.js and Express app that makes the preview image shown when a link is shared on X, LinkedIn, Slack or iMessage. Enter a title, description and author, pick your colors, watch the live preview, and download a 1200×630 PNG. Start from the blog post, product or event layouts, or change the card’s HTML to match your brand.

What it does
- Live preview
- See the card change as you type, before you spend a request rendering it.
- The right size
- Renders at 1200×630, the size Open Graph and X large image cards expect.
- Safe to deploy
- The server builds the card from the fields and escapes them, so nobody can render their own HTML on your key.
- Easy to restyle
- The card is plain HTML and CSS in one function, so adding your logo or fonts is a small edit.
How it works
The card template and the route that renders it.
// The card is built here, not in the browser. Accepting raw HTML would let anyone
// render any page they like on your API key.
const HEX = /^#[0-9a-f]{6}$/i;
const esc = (s) => s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
function shade(hex, amount) {
const n = parseInt(hex.slice(1), 16);
const c = (v) => Math.min(255, Math.max(0, v + amount));
return '#' + ((c(n >> 16) << 16) | (c((n >> 8) & 0xff) << 8) | c(n & 0xff)).toString(16).padStart(6, '0');
}
function cardHtml({ title, description, author, bgColor, textColor }) {
return `<!DOCTYPE html><html><head><style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { width: 1200px; height: 630px; background: linear-gradient(135deg, ${bgColor} 0%, ${shade(bgColor, -30)} 100%);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; flex-direction: column;
justify-content: center; padding: 80px; color: ${textColor}; }
h1 { font-size: 64px; font-weight: 700; margin-bottom: 24px; line-height: 1.2; }
p { font-size: 32px; opacity: 0.9; margin-bottom: 40px; line-height: 1.4; }
.author { font-size: 24px; opacity: 0.7; display: flex; align-items: center; gap: 12px; }
.dot { width: 8px; height: 8px; background: ${textColor}; border-radius: 50%; opacity: 0.5; }
</style></head><body>
<h1>${esc(title)}</h1><p>${esc(description)}</p><div class="author"><span class="dot"></span>${esc(author)}</div>
</body></html>`;
}
// POST /api/generate { title, description, author, bgColor, textColor }
app.post('/api/generate', async (req, res) => {
const fields = {
title: str(req.body.title, 90) || 'Title',
description: str(req.body.description, 160) || 'Description',
author: str(req.body.author, 60) || 'Author',
bgColor: HEX.test(req.body.bgColor) ? req.body.bgColor : '#667eea',
textColor: HEX.test(req.body.textColor) ? req.body.textColor : '#ffffff'
};
try {
const data = await callApi('htmltoimage', { body: { html: cardHtml(fields), width: 1200, height: 630, format: 'png' } });
res.json({ success: true, imageUrl: data.downloadURL, width: 1200, height: 630, format: 'png' });
} catch (err) {
res.status(err.status || 502).json({ error: err.message });
}
});Deploy it
- Get a free API key
The free plan includes 200 credits a month, no card needed. Create your key.
- Deploy it
Click Deploy to Vercel. Vercel copies the repo to your GitHub account and asks for
APIVERVE_API_KEY: paste your key there. - Open your app
Vercel builds it and gives you a live URL, usually in about a minute. Every push to the repo redeploys it.
Try the HTML to Image API — the call this template makes
No key required to try it. Get a key to use it in your app.
{
"status": "ok",
"error": null,
"data": {
"width": 800,
"height": 600,
"format": "png",
"imageName": "bf3d3d67-bc6e-477b-a916-560b895f8091.png",
"expires": 1789676439207,
"downloadURL": "https://storage.googleapis.com/apiverve/APIData/htmltoimage/bf3d3d67-bc6e-477b-a916-560b895f8091.png?GoogleAccessId=635500398038-compute%40developer.gserviceaccount.com&Expires=1789676439&Signature=Uw7imp8TVuygEgWhdLAmK8%2Byk0NoZmtdLEX9MrDHjlWDYyB28y7zbCxVuD%2BF%2F6Iij5zi%2FhvDoJnuhNT6a2Yl85gw5nWGt750Ai4gup46dQQt0vrsUpD99IPTfTh21DWd4WqE7VCWlIlou7WwNUn620WgWM3ScGi13dCB81Qyb4WuxrdcNal8WrUYX9nZ12h%2B7BZs65Tyw0V4StbvsYxXKqySkF4aQ8J1QbdhUny9WyptE3qNT%2FyM4m2%2F%2FJAOq32bQsSWj8%2BycWEhtifIgpJe4LTBBeqtHBePrIQSrYNRdOwhjaTzj%2BShSeox5wPztiaoRyi1DGIx4FIIstS2dAdQ6Q%3D%3D",
"htmlLength": 197
}
}