How to Automatically Generate Social Sharing Images
Generate page-specific social sharing images with a Next.js route or an image transformation service, then publish them in Open Graph metadata.
Automatically generate social sharing images by rendering a card from each page’s data or transforming an existing image, then publishing the resulting public image URL in that page’s social metadata. For a Next.js app, a route using Vercel’s @vercel/og and ImageResponse is a direct way to generate a dynamic card. If your images already live in Cloudinary, its image transformation URLs can compose a card from those assets. In either approach, social crawlers must be able to fetch the image.
1. Choose a generation approach
| Approach | Use it when | Check before shipping |
|---|---|---|
| Code-rendered route, such as Vercel OG | Your app has page-specific titles, descriptions, or other data to render into a custom layout, especially in a Next.js/Vercel stack. | Runtime and framework compatibility, supported CSS and fonts, bundle size, route access, and cache behavior. |
| Image transformation service, such as Cloudinary | You already store source images there or want to assemble cards using managed resize, crop, text overlay, and graphic transformations. | Source asset availability, transformation URL behavior, delivery access, and current service terms. |
These are different implementation workflows, not a measured performance or cost comparison. Vercel’s documentation describes its own renderer; Cloudinary’s articles describe its own transformation workflow. Choose based on your existing assets, hosting stack, desired layout, and operational constraints.
2. Generate a dynamic card with Next.js and Vercel OG
Vercel documents using @vercel/og and ImageResponse in a route to render social card images. Route parameters can supply changing content. The documented setup lists Node.js 22 or newer and Next.js 12.2.3 or newer; the Next.js App Router includes the package. Verify these details against the framework version you deploy. See the Vercel OG image generation documentation.
Create the image route
For an App Router project, create app/og/route.tsx. This minimal route uses a title query parameter and returns a PNG. It validates the input length and falls back to a safe title:
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const requestedTitle = searchParams.get('title') ?? 'Example page';
const title = requestedTitle.trim().slice(0, 100) || 'Example page';
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
background: '#101827',
color: '#ffffff',
padding: '64px',
fontSize: 64,
fontWeight: 700,
}}
>
{title}
</div>
),
{ width: 1200, height: 630 },
);
}
The renderer supports a subset of CSS. Vercel documents flexbox support and no CSS grid support, and lists ttf, otf, and woff as supported font formats. Its documented bundle limit is 500 KB. Keep the design within those constraints and check current documentation before depending on a specific style or runtime feature.
Use page data and publish metadata
Set the metadata on each page so its title and description match the content used to generate its card. The image URL should be absolute and publicly fetchable. In the App Router, a page can export metadata like this:
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
const imageUrl = new URL('https://example.com/og');
imageUrl.searchParams.set('title', post.title);
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
images: [{ url: imageUrl.toString(), width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
images: [imageUrl.toString()],
},
};
}
Replace getPost and example.com with your app’s data loader and deployed public origin. Vercel’s deployment metadata guidance allows twitter:image to point to a static or dynamic image and describes og:image as a fallback. See Vercel deployment metadata.
3. Generate cards with Cloudinary transformations
If your source imagery is already in Cloudinary, its transformation API can create a social image URL by combining operations such as resizing, cropping, text overlays, and graphics. The resulting URL is what you place in the page’s og:image metadata. Consult Cloudinary’s guides for creating social media images and image transformations for the current URL syntax and options.
A transformed image route still needs page-specific values: choose the source asset, set a consistent crop and dimensions, and encode or otherwise safely construct any dynamic text parameters according to Cloudinary’s current syntax. Avoid accepting arbitrary transformation fragments or untrusted URLs from a public query parameter. The exact URL depends on your account, asset identifiers, and transformation setup, so use the vendor’s current documentation rather than copying an account-specific URL from an example.
4. Choose dimensions and design for previews
Vercel recommends 1200×630 pixels for OG images. A 2022 Cloudinary article gives 1200×627 pixels as the most frequently recommended size. These recommendations differ slightly; neither should be treated as a universal platform mandate. Check the current requirements and preview behavior of the social platforms you target.
- Keep essential text and visual details away from the outer edges, where previews may crop.
- Use strong contrast and a short page-specific title that remains legible when the preview is shown small.
- Test long titles, missing images, unusual characters, and pages with no custom card data.
- Use the same canonical page data for the HTML metadata and generated card to avoid mismatched titles.
5. Make the image fetchable and cache it correctly
A valid metadata tag is not enough if the social platform cannot fetch the image. Confirm that the image URL is public, returns an image response, does not require cookies or authorization, and does not redirect to a blocked or inaccessible destination. Vercel recommends allowing its OG image API routes in robots.txt so social providers can fetch them. This crawler access check does not guarantee that every platform will refresh or display a preview in the same way.
Vercel says @vercel/og adds CDN caching headers to reduce recomputation and cost. For parameterized image URLs, make sure the cache key includes every value that changes the rendered card. If page content changes, decide how the URL or cache lifetime will cause a refreshed image to appear. Validate externally supplied values, constrain their length, and avoid allowing a route parameter to fetch arbitrary remote resources.
6. Verify the result
- Open the generated image URL directly in a private browser window. Confirm it returns the expected image without authentication.
- View the page source or rendered metadata and check that
og:imagecontains the absolute image URL. Checktwitter:imageif you set it explicitly. - Check the route’s robots rules and any hosting access controls that could prevent crawler requests.
- Try representative pages: short and long titles, non-ASCII characters, missing data, and updated content.
- Use the intended platform’s current preview/debugging workflow to check actual crawler behavior. A browser rendering successfully does not prove a platform crawler can retrieve it.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No image appears in the preview | The metadata is absent, malformed, relative, or points to an inaccessible URL. | Use an absolute public URL, inspect the rendered page metadata, and request the image without a logged-in session. |
| The image route returns an error | The deployed Node.js or Next.js version is incompatible, or the route uses unsupported renderer features. | Check the documented runtime and framework requirements, then simplify the JSX and CSS to supported features. |
| Text or layout is missing | The template relies on CSS Grid or an unsupported style or font format. | Use supported flexbox layout and a documented font format such as TTF, OTF, or WOFF; verify current support. |
| Build fails or the renderer exceeds its limit | The image route bundle exceeds the documented 500 KB limit. | Remove unnecessary dependencies and assets, reduce font files, and keep route code focused. |
| Old card keeps appearing | The image URL is cached by a CDN or social platform. | Change a content version in the URL when the card changes, review cache headers, and use the platform’s current refresh/debugging mechanism. |
| Some crawlers cannot retrieve the image | Robots rules, authentication, redirects, or hosting restrictions block the request. | Allow the image route in robots rules and make its response publicly accessible over HTTPS. |
| Dynamic title breaks the card | The title is too long or contains values the layout was not designed to handle. | Validate and bound input length, define a fallback, and test wrapping or truncation with representative content. |
8. Performance, reliability, and cost
On-demand rendering spends work when a card is requested; caching can reduce repeated rendering. Vercel documents CDN caching headers for its OG renderer, but actual latency and cost depend on deployment, cache hits, traffic, and the chosen hosting plan. The dossier does not establish an independent performance or cost comparison between rendering and transformation services.
For reliability, ensure the image route can render without depending on fragile page state, use stable inputs and fallbacks, and keep external fetches out of the critical rendering path where possible. For content that changes infrequently, a stable versioned image URL with deliberate cache behavior can avoid repeated regeneration. For transformations, account for the availability of source assets and the service’s current delivery and transformation terms.
Or skip the browser setup
ScreenshotNeo can capture a URL with one API request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. It also provides an MCP server with screenshot, page-info, and PDF tools for AI agents.
For example, this cURL request saves a WebP screenshot:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
See the ScreenshotNeo API documentation for options. ScreenshotNeo offers 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000. Sign up for free.
FAQ
Does generating an OG image automatically update every existing social preview?
No. The page must reference the image in its metadata, and each platform controls when it fetches or refreshes that metadata.
Should I use an image route or a transformation URL?
Use a route when the card is primarily composed from page data and custom layout. Use transformations when existing managed assets are the starting point and the desired card can be composed from them.
Can my social image URL be relative?
Use an absolute public URL so crawlers know where to fetch the image.
Do I need both og:image and twitter:image?
Set the metadata required by your targets. Vercel’s guidance supports a static or dynamic twitter:image and describes og:image as a fallback.
Sources
- Vercel: OG Image Generation
- Vercel: Deployment Metadata
- Vercel: Allowing OG routes in robots.txt
- Cloudinary: How to Create Social Media Images
- Cloudinary: Image Transformation
ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. See ScreenshotNeo for the product overview.


