How to Embed Live Website Thumbnails in Your App

Build rich link preview cards like Notion, Slack, and Twitter by generating live website thumbnails on the fly.

đź“‹ Table of Contents

Why Website Thumbnails Matter

Every modern productivity and communication tool displays rich visual previews when a user pastes a URL. Think of how Notion renders a visual card when you embed a link, or how Slack unfurls a URL with an image preview alongside the page title.

These visual thumbnails dramatically improve user engagement. Studies show that content with visual previews gets 94% more views than plain-text links. For SaaS applications, bookmark managers, CMS platforms, and social feeds, website thumbnails are no longer a nice-to-have—they are expected by users.

Common Approaches

There are three common ways to generate website thumbnails:

Building a Backend Thumbnail Proxy

The recommended architecture is to build a lightweight proxy endpoint on your backend that requests a thumbnail from the Screenshot API and then serves or caches the result. This keeps your API key secure and gives you full control over caching.

// Express.js thumbnail proxy endpoint
const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.get('/api/thumbnail', async (req, res) => {
  const { url } = req.query;

  if (!url) {
    return res.status(400).json({ error: 'URL parameter is required' });
  }

  try {
    const apiUrl = new URL('https://snapapi.alterra-solutions.com/v1/screenshot');
    apiUrl.searchParams.append('url', url);
    apiUrl.searchParams.append('width', '1280');
    apiUrl.searchParams.append('height', '720');
    apiUrl.searchParams.append('format', 'webp'); // Smaller file size
    apiUrl.searchParams.append('api_key', process.env.SNAPAPI_KEY);

    const response = await fetch(apiUrl.toString());

    if (!response.ok) {
      throw new Error('Screenshot API request failed');
    }

    const imageBuffer = await response.buffer();

    // Cache for 24 hours
    res.setHeader('Content-Type', 'image/webp');
    res.setHeader('Cache-Control', 'public, max-age=86400, s-maxage=86400');
    res.send(imageBuffer);
  } catch (error) {
    res.status(500).json({ error: 'Failed to generate thumbnail' });
  }
});

app.listen(3000);

React Frontend Component

On the frontend, you can create a reusable <WebsiteThumbnail /> React component that fetches and displays the thumbnail through your backend proxy. This provides a clean developer experience across your app.

import React, { useState } from 'react';

function WebsiteThumbnail({ url, alt }) {
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);

  // Generate the proxy URL
  const thumbnailSrc = `/api/thumbnail?url=${encodeURIComponent(url)}`;

  if (hasError) {
    return (
      <div className="thumbnail-fallback">
        <span>đź”—</span>
        <p>{new URL(url).hostname}</p>
      </div>
    );
  }

  return (
    <div className="thumbnail-card">
      {isLoading && <div className="thumbnail-skeleton" />}
      <img
        src={thumbnailSrc}
        alt={alt || `Preview of ${url}`}
        loading="lazy"
        onLoad={() => setIsLoading(false)}
        onError={() => setHasError(true)}
        style={{ display: isLoading ? 'none' : 'block' }}
      />
      <div className="thumbnail-meta">
        <span>{new URL(url).hostname}</span>
      </div>
    </div>
  );
}

// Usage
<WebsiteThumbnail url="https://github.com" alt="GitHub Homepage" />

Caching for Performance

Generating a fresh screenshot for every request is wasteful and slow. You should implement a multi-layer caching strategy:

// Redis caching layer example
const Redis = require('ioredis');
const redis = new Redis();

async function getCachedThumbnail(url) {
  const cacheKey = `thumb:${url}`;
  const cached = await redis.getBuffer(cacheKey);

  if (cached) {
    console.log('Cache HIT');
    return cached;
  }

  console.log('Cache MISS — fetching from API');
  const imageBuffer = await fetchFromSnapAPI(url);

  // Cache for 24 hours
  await redis.set(cacheKey, imageBuffer, 'EX', 86400);
  return imageBuffer;
}

With this setup, you can serve website thumbnails to thousands of users at near-zero latency and minimal API cost.

Start Embedding Website Thumbnails

100 free screenshots per month. Perfect for building your link preview feature.

Get Started Free →