ScreenshotNeo

BlogHow-to

How to Make Open Graph Images for Product Pages Automatically

Generate a unique Open Graph image for each product page from catalog data, then publish its URL in the page metadata. Includes Next.js and Shopify approaches.

By the ScreenshotNeo team4 October 20269 min read

Generate one image per product from catalog data, then publish its absolute URL as that product page’s og:image metadata. In Next.js, use the opengraph-image file convention and ImageResponse to create route-specific images. In Shopify, first check whether the theme already uses the product image for Open Graph metadata; Shopify can generate relevant tags automatically when the theme does not include them.

An Open Graph (OG) image represents a page when its URL is shared. The image needs to be associated with the product page in metadata, and both the metadata and the image URL must be publicly fetchable by social crawlers. Choose the implementation based on where product data is available, how much design control you need, and how you will refresh cached images when products change. See the official Next.js metadata and OG images guide and Shopify’s page_image reference.

1. Choose the approach

Approach Use it when Check before shipping
Next.js generated image route Your product data is available to the Next.js route and you want a custom image generated for each product URL. Route parameters and data access, caching and revalidation, supported CSS and assets, runtime, and image dimensions.
Vercel OG generation You want to generate images from HTML and CSS in a hosted function. Runtime and hosting constraints, font and image loading, crawler access to the route, and cache invalidation.
Shopify theme metadata The product’s existing image is suitable as the social preview, or you already manage metadata in the theme. Whether the theme already emits tags, which image it selects, and whether the output matches your desired preview.

For a standard Shopify product page, inspect the rendered HTML before adding custom tags. Shopify documents that it automatically generates relevant tags when the theme does not include them. If you need a composed design with product name, brand or category, and a product photo, use the generation path that can access those fields reliably.

2. Generate product images in Next.js

The App Router supports a route-specific opengraph-image.tsx file. It can receive route parameters, load the product, and return an image with ImageResponse. The following example assumes a route at app/products/[slug]/opengraph-image.tsx and a server-side product lookup function. Replace the lookup with your catalog or database access; keep it server-side and use a stable product identifier.

// app/products/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
import { getProductBySlug } from '@/lib/products'

export const runtime = 'edge'

export const alt = 'Product preview'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

type Props = {
  params: Promise<{ slug: string }>
}

export default async function OpenGraphImage({ params }: Props) {
  const { slug } = await params
  const product = await getProductBySlug(slug)

  const name = product?.name?.trim() || 'Product'
  const category = product?.category?.trim() || 'Featured product'

  return new ImageResponse(
    (
      <div
        style={{
          width: '100%',
          height: '100%',
          display: 'flex',
          flexDirection: 'column',
          justifyContent: 'space-between',
          padding: 72,
          background: '#f5f3ee',
          color: '#171717',
          fontFamily: 'sans-serif',
        }}
      >
        <div style={{ display: 'flex', fontSize: 28, color: '#5b5b5b' }}>
          {category.slice(0, 48)}
        </div>
        <div
          style={{
            display: 'flex',
            fontSize: 64,
            lineHeight: 1.08,
            fontWeight: 700,
            maxWidth: 1000,
          }}
        >
          {name.slice(0, 72)}
        </div>
      </div>
    ),
    size,
  )
}

Use the current parameter typing for your installed Next.js version; Next.js versions differ in how route parameters are typed. The example deliberately truncates catalog text so unusually long names do not overwhelm the layout. For a missing product, return a clear fallback image or handle not-found behavior consistently with the product route. The generated file convention associates the generated image with the route’s metadata.

If you prefer to set metadata explicitly, a page can return the image route in generateMetadata. Ensure the URL is absolute in the final HTML, especially if setting an image URL manually.

// app/products/[slug]/page.tsx
import type { Metadata } from 'next'
import { getProductBySlug } from '@/lib/products'

export async function generateMetadata(
  { params }: { params: Promise<{ slug: string }> },
): Promise<Metadata> {
  const { slug } = await params
  const product = await getProductBySlug(slug)
  const origin = process.env.SITE_ORIGIN

  if (!product || !origin) {
    return { title: product?.name ?? 'Product' }
  }

  return {
    title: product.name,
    openGraph: {
      type: 'website',
      title: product.name,
      images: [`${origin}/products/${encodeURIComponent(slug)}/opengraph-image`],
    },
  }
}

For newer Next.js versions, check the documented file convention and metadata behavior for your exact version. The file-based image convention can provide metadata automatically; avoid adding duplicate tags without checking the rendered document.

3. Render a custom image with Vercel OG

Vercel’s OG generation supports creating images from HTML and CSS and documents CDN cache headers. It also documents Node.js runtime support and a Pages Router plus Node.js syntax caveat. Confirm current runtime requirements for your framework and deployment. Keep the route reachable to social crawlers, including in robots.txt, and ensure any external fonts or product images can be fetched by the generation runtime.

Use this option when you need a separate generation endpoint or are already using its hosting model. The template should use product data loaded on the server, with text length constraints and a fallback for absent fields just as in the Next.js file convention. See Vercel’s OG Image Generation documentation for the current integration and runtime details.

4. Use Shopify’s existing product image metadata

Shopify’s Liquid page_image object can be used to create Open Graph image tags. Shopify says it generates relevant tags automatically when the theme does not include them. Inspect the product page’s returned HTML to learn whether the theme already emits og:image and which image it chooses. If the existing featured or configured social image is suitable, no separate image generator may be necessary.

If you add theme-level metadata yourself, use the product’s intended image and output its public URL in the head. Avoid emitting two competing og:image tags unintentionally. For a composed image containing text or graphics, use theme or application code that fits the store’s architecture, then verify the generated asset is publicly retrievable.

5. Design the template and data flow

  1. Choose stable inputs. A product ID or slug, name, brand/category, and product image are typical inputs. Avoid mutable details such as live price unless you have a cache refresh strategy.
  2. Make data available server-side. The metadata function or image route must be able to retrieve the product without relying on browser-only state or a client-side API call.
  3. Constrain content. Trim and length-limit names and labels. Account for missing images, empty categories, punctuation, and unexpected text. Do not let catalog content control arbitrary markup or styling.
  4. Keep a fallback. A missing record or image should produce deliberate not-found behavior or a useful default graphic, not a broken image response.
  5. Publish an absolute, public URL. Social crawlers need to fetch the metadata and image from outside your application session. Do not require authentication or a user cookie.
  6. Decide freshness rules. Generated images are commonly cached. Define how a title, image, or other displayed field change invalidates or revalidates the generated result.

A simple product card often needs only the product name, brand or category, and product image. Keep the composition readable at preview size. If adding a price, sale badge, or availability, make sure stale cached images will not misrepresent changing catalog state.

6. Verify the page and image

  1. Request the product page without a logged-in browser session and inspect its initial HTML for the expected og:image URL.
  2. Confirm the URL is absolute, unique where appropriate, and points to the expected product image route or asset.
  3. Fetch the image URL directly. Check that it returns an image response rather than an HTML error page or a redirect requiring authentication.
  4. Open the generated image and check text clipping, missing product data, and the appearance at social preview size.
  5. Use the target social platform’s preview or debugging tool to confirm what it fetched. A browser-rendered page can differ from the initial HTML crawlers receive.
  6. After changing a product’s title or image, check whether the cached preview updates according to your chosen revalidation strategy.

For automated checks, cover a normal product, a very long name, missing optional fields, a missing product, and a product image that cannot be fetched. Assert that the page’s metadata points to a reachable image and that the image route returns the expected content type.

7. Caching, performance, and reliability

Next.js documents that file-based generated images are statically optimized and cached by default unless dynamic APIs or uncached data change that behavior. Vercel documents CDN caching for its OG generation library. Caching helps avoid regenerating an identical card for every crawler fetch, but it means edits may not appear immediately.

  • Use stable cache inputs. If the image depends on a product title or image, make cache invalidation or revalidation follow those changes. A versioned image URL is another design option when your system can publish one.
  • Keep generation self-contained. Limit remote lookups and assets on the critical path. Handle upstream catalog failures with a deliberate fallback or an error response that can be retried.
  • Make crawler access reliable. Check route access rules, redirects, robots policy, and public asset permissions. Vercel recommends allowing social crawler access to image routes in robots.txt.
  • Review runtime constraints. Check the current Next.js version, route runtime, supported CSS and asset formats, and platform limits for your deployment.
  • Avoid fragile content. Long names, missing images, external font failures, or transient product API errors should not yield a malformed card.

The official documentation reviewed does not provide a measured engagement or traffic uplift from generated OG images. Treat this as a metadata and presentation implementation; verify your own social previews rather than assuming a performance benefit.

8. Troubleshooting

Symptom Likely cause Fix
No image appears in the preview The initial HTML has no og:image, or the crawler cannot fetch its URL. Inspect returned HTML, use an absolute public URL, and check redirects, access controls, and crawler rules.
Preview shows an old product name or image The generated image or CDN response is cached. Review the route’s static/dynamic behavior and configure revalidation or invalidation for catalog updates.
Image route returns an error for one product Missing data, an unexpected slug, or a failed upstream lookup. Validate route parameters, handle missing products deliberately, and add a fallback for optional fields.
Long name clips or dominates the card Unbounded text was passed into a fixed layout. Trim or clamp the text, reduce type size where needed, and test long catalog names.
Product photo or font is absent The asset is private, unreachable from the generation runtime, or unsupported by the current setup. Use a publicly fetchable supported asset, confirm runtime compatibility, and provide a fallback.
Shopify page has duplicate or unexpected metadata The theme already emits tags or selects a different image source. Inspect actual rendered head output and adjust the theme’s existing metadata path rather than adding a conflicting tag.
Social debugger cannot access the generated route robots.txt, route restrictions, authentication, or deployment redirects block the crawler. Allow crawler access to the image route and verify a direct public fetch.

9. Or skip the browser setup

If your task is to capture how a product page currently renders, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; it is useful for visual checks alongside OG metadata generation, not a replacement for publishing the page’s og:image tag.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Replace the example URL with your product page. See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, no card required.

10. FAQ

Does each product need a separate image file?

No. A route can generate an image from that product’s data when requested, subject to the framework and hosting cache behavior. The product page still needs metadata that associates the image URL with that product.

Should the Open Graph image include the product price?

Only if you are prepared to keep cached images current when the price changes. A name, category, and product image are less volatile inputs.

Can I use the same image for every product?

You can, but route-specific images make it possible to represent each product distinctly. Use a shared fallback for missing data if needed.

Will a client-side metadata update work for social crawlers?

Use the framework’s server-side metadata or file convention and verify the initial response HTML and public image URL. Do not assume a crawler will run browser-side application code.