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.

The social card generator app running

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.

server.js
// 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) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[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 });
  }
});

See the whole file on GitHub

Deploy it

  1. Get a free API key

    The free plan includes 200 credits a month, no card needed. Create your key.

  2. Deploy it

    Click Deploy to Vercel. Vercel copies the repo to your GitHub account and asks for APIVERVE_API_KEY: paste your key there.

  3. 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

Request
POSTapi.apiverve.com/v1/htmltoimage
Body
Verification
Format

No key required to try it. Get a key to use it in your app.

Build your own with HTML to Image. Start with 200 free credits. One key unlocks all 300+ APIs.

Explore the catalog

300+ APIs on the same key and the same response shape.

Browse all APIs