Building Rich Link Previews
Link previews transform plain URLs into engaging visual cards that drive clicks. Screenshot-based previews are more accurate and compelling than OG-tag-only previews because they show the actual page content.
Architecture
A link preview service follows this pattern:
- User submits a URL
- Backend captures a screenshot at preview dimensions (1200x630)
- Screenshot is cached with a unique key derived from the URL
- Cached image is served for all subsequent requests
- Cache refreshes periodically based on a TTL
Implementation
import express from 'express';
import crypto from 'crypto';
const app = express();
const cache = new Map();
app.get('/preview', async (req, res) => {
const url = req.query.url;
if (!url) return res.status(400).json({ error: 'URL required' });
const key = crypto.createHash('sha256').update(url).digest('hex');
if (cache.has(key)) {
const { image, timestamp } = cache.get(key);
if (Date.now() - timestamp < 86400000) { // 24h cache
res.set('Content-Type', 'image/jpeg');
return res.send(image);
}
}
const ssRes = await fetch(
`https://apisnap.dev/api/screenshot?url=${encodeURIComponent(url)}&format=jpeg&quality=80&width=1200&height=630`,
{ headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
);
if (!ssRes.ok) return res.status(502).json({ error: 'Preview failed' });
const image = Buffer.from(await ssRes.arrayBuffer());
cache.set(key, { image, timestamp: Date.now() });
res.set('Content-Type', 'image/jpeg');
res.send(image);
});Optimization Tips
Use Redis or Memcached for shared caching across instances. Set appropriate TTLs: 24 hours for news sites, 7 days for static pages. Always have a fallback image for failed captures. Use JPEG format for link previews to keep file sizes small.