How to build link previews for your app

How to build link previews for your app

The architecture behind those URL cards in Slack, Discord, and every messaging app.

March 28, 2026 · 9 min read

When you paste a URL in Slack, Discord, iMessage, or Twitter, a preview card appears — a title, description, thumbnail, and sometimes a site icon. These link previews are one of the most common uses of screenshot APIs and OG tag parsing. Here's how to build your own.

How link previews work

The typical flow when a user pastes a URL:

  1. Your backend receives the URL
  2. You fetch the page and parse its HTML for Open Graph tags (og:title, og:description, og:image)
  3. If OG tags exist, use them for the preview card
  4. If OG tags are missing or incomplete, fall back to a screenshot thumbnail
  5. Cache the result so you don't re-fetch on every view

Step 1: Fetch and parse OG tags

Node.js
import { JSDOM } from 'jsdom';

async function getOGTags(url) {
  const response = await fetch(url, {
    headers: { 'User-Agent': 'LinkPreviewBot/1.0' },
    signal: AbortSignal.timeout(10000),
  });
  const html = await response.text();
  const dom = new JSDOM(html);
  const doc = dom.window.document;

  const getMeta = (property) =>
    doc.querySelector(`meta[property="${property}"]`)?.content ||
    doc.querySelector(`meta[name="${property}"]`)?.content;

  return {
    title: getMeta('og:title') || doc.title || url,
    description: getMeta('og:description') || getMeta('description') || '',
    image: getMeta('og:image') || null,
    siteName: getMeta('og:site_name') || new URL(url).hostname,
    favicon: `https://www.google.com/s2/favicons?domain=${new URL(url).hostname}&sz=32`,
  };
}

Step 2: Screenshot fallback

Not every page has OG tags. For pages without an og:image, generate a thumbnail screenshot:

async function getScreenshotThumbnail(url) {
  const response = await fetch(
    'https://api.nightglass.xyz/api/v1/screenshot',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.NIGHTGLASS_KEY}`,
      },
      body: JSON.stringify({
        url,
        width: 1200,
        height: 630,
        format: 'jpeg',
        quality: 75,
      }),
    }
  );
  return Buffer.from(await response.arrayBuffer());
}

Step 3: Combine into a preview service

async function getLinkPreview(url) {
  const og = await getOGTags(url);

  // If no OG image, generate a screenshot
  if (!og.image) {
    const screenshot = await getScreenshotThumbnail(url);
    const screenshotUrl = await uploadToStorage(screenshot, `previews/${hash(url)}.jpg`);
    og.image = screenshotUrl;
  }

  return og;
}

Caching strategy

Link previews should be cached aggressively. Once generated, the preview for a URL rarely changes. Use a two-tier cache:

Key by URL hash. When a user pastes a URL, check cache first. If miss, generate in the background and return a placeholder immediately — don't block the message send on preview generation.

Queue architecture

For apps with many users pasting URLs, preview generation should be async. When a URL is pasted, enqueue a job to generate the preview. The message renders immediately with the URL text, and the preview card appears a few seconds later when the job completes.

This prevents slow or broken URLs from blocking the user experience. If a page takes 15 seconds to load, the user doesn't wait — the preview arrives when it's ready.

Rate limiting and abuse prevention

Your preview service will be called for every URL your users paste. Consider: rate limiting by user (prevent spam), URL validation (block internal/private IPs), timeout handling (don't wait forever for slow pages), and content filtering (don't generate previews for known malicious domains).

nightglass handles the hard part. The screenshot generation, browser management, and rendering complexity is handled by the API. Your link preview service just needs to do OG tag parsing and API calls. See the quickstart guide to get set up.

If generating custom social media OG images from templates, the OG image guide covers the HTML template + screenshot pattern. For full-page captures, see the full-page screenshot guide. Evaluating which API to use? 2026 API comparison.