The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Next.js supports Open Graph images in two ways: add an opengraph-image file to an App Router segment for a fixed graphic, or add an opengraph-image.js, .ts, or .tsx route that returns a generated image. Next.js discovers the file, adds the corresponding metadata to the document head, and uses the most specific route-segment image for each URL.
This guide shows both approaches, including dynamic route data, version-sensitive code, size limits, caching behavior, validation, and practical troubleshooting.
Choose the right Open Graph implementation
| Requirement | Recommended approach | Why |
|---|---|---|
| One default image for the whole site | app/opengraph-image.jpg |
Minimal setup and no rendering code |
| A default image for a section | Place the file in that nested segment | Nested images override higher-level images |
| Title, author, price, or other page data in the image | app/.../opengraph-image.tsx |
The route can read parameters and fetch data |
| Frequent design changes by non-developers | Static assets or an external image service | Less rendering and dependency complexity |
Next.js documents the opengraph-image convention for App Router projects in its metadata and OG images guide and file-convention reference.
Add a static Open Graph image
Site-wide default
- Create or use the root App Router segment:
app/. - Add one of the documented files:
app/opengraph-image.jpg,.jpeg,.png, or.gif. - Run the development server or build. Next.js generates the appropriate Open Graph metadata for routes covered by that segment.
For example:
app/
layout.tsx
page.tsx
opengraph-image.jpg
The documented maximum for an opengraph-image file is 8 MB. A larger file causes a build failure, so compress the source image before committing it. The separate twitter-image convention has a different documented 5 MB limit.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
Section-specific defaults
Put the image in the segment that owns the section:
app/
opengraph-image.jpg
docs/
opengraph-image.png
page.tsx
blog/
opengraph-image.jpg
page.tsx
A route-specific image in a deeper segment takes precedence over an image in a higher segment. This lets the root image act as a fallback while sections provide their own branding.
Generate a dynamic image with ImageResponse
Use a code file when each page needs a different title or other data. Export a default function and return ImageResponse from next/og. The official example uses 1200 by 630 pixels; treat that as the documented example/default rather than a guarantee made by every social network.
Static text in a generated image
// app/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const alt = 'Acme documentation'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default function Image() {
return new ImageResponse(
(
<div
style={{
background: '#111827',
color: 'white',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
padding: '80px',
width: '100%',
height: '100%',
}}
>
<div style={{ fontSize: 72, fontWeight: 700 }}>Acme Docs</div>
<div style={{ fontSize: 32, marginTop: 24 }}>Build better software</div>
</div>
),
{ ...size },
)
}
The optional alt, size, and contentType exports populate related metadata. Keep the JSX and styles within the renderer’s supported subset.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Use a dynamic route parameter
A route such as app/posts/[slug]/opengraph-image.tsx can render a page-specific title. Current Next.js 16 file-convention documentation makes params a promise. If your installed version uses the earlier synchronous form, follow that version’s API reference instead.
// app/posts/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetch(`https://example.com/api/posts/${slug}`, {
next: { revalidate: 3600 },
}).then((r) => {
if (!r.ok) throw new Error(`Post request failed: ${r.status}`)
return r.json() as Promise<{ title: string }>
})
return new ImageResponse(
(
<div style={{ background: 'white', display: 'flex', flexDirection: 'column', padding: 72, width: '100%', height: '100%' }}>
<div style={{ color: '#2563eb', fontSize: 30 }}>Acme Blog</div>
<div style={{ color: '#111827', fontSize: 64, fontWeight: 700, marginTop: 32 }}>{post.title}</div>
</div>
),
{ ...size },
)
}
Validate and escape data before rendering it. Handle missing records explicitly; an unhandled fetch error turns the image request into a failure.
ImageResponse constraints and design details
The Next.js 15 API reference states that ImageResponse uses @vercel/og, Satori, and Resvg to convert HTML/CSS into PNG. It supports flexbox and a subset of CSS; advanced layouts such as CSS Grid are not supported. The same versioned reference documents a 500 KB maximum bundle size and support for TTF, OTF, and WOFF fonts. These are version-specific constraints, so check the API reference for the Next.js version in your project: Next.js 15 ImageResponse reference.
- Prefer flexbox, explicit dimensions, and simple typography.
- Keep imports and embedded assets small enough for the documented bundle limit.
- Load fonts as binary data when you need a custom typeface, and verify the font format is supported by your version.
- Design for the declared dimensions; long titles need truncation, wrapping, or a smaller font.
Metadata, precedence, and caching
Next.js adds metadata for the discovered image route; you do not normally need to hand-write an og:image tag. A deeper segment wins when multiple opengraph-image files apply. Inspect the rendered HTML and the image URL in a production build to confirm which segment supplied the image.
Recommended Free Tools
Rank #3
Generated metadata image routes are cached and statically optimized by default. Request-time APIs, uncached data, or dynamic route configuration can change that behavior. Do not assume every generated image is rendered on every request. Choose a revalidation strategy that matches how quickly the underlying page data changes, and avoid an uncached database call for every crawler request unless it is necessary.
Test an Open Graph image before publishing
- Start the app in the same mode you deploy, preferably with a production build.
- Open the route’s generated image URL directly and confirm it returns an image, not an HTML error page.
- Inspect the page source or response metadata for the generated
og:imageURL. - Check that the URL is publicly reachable over HTTPS and does not depend on a logged-in browser session.
- Test a route with a long title, missing data, non-ASCII characters, and a nested segment to expose layout and precedence bugs.
- After deployment, remember that social platforms may cache previews independently; Next.js does not guarantee immediate refresh or display on every service.
Common failures and fixes
No image metadata appears
Confirm the file is inside app/ (not an unrelated directory), uses a documented filename and extension, and belongs to the route segment you are testing. Restart the development server after adding a convention file.
The build fails because the file is too large
Compress or resize the static asset below the documented 8 MB opengraph-image limit. Do not apply the separate Twitter limit to this file.
The generated route returns an error
Check the server log for failed fetches, invalid JSON, missing route parameters, unsupported CSS, or an exception while loading fonts. Return a valid ImageResponse on every successful path and handle a missing record with a fallback title or a deliberate 404.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
The title is clipped or the layout is blank
Use flexbox and explicit sizes, reduce font size for long values, and remove unsupported CSS Grid or browser-only APIs. The renderer is not a full browser.
Changes do not appear
Generated routes can be cached or statically optimized. Review your fetch caching and revalidation settings, rebuild when appropriate, and account for the external platform’s own preview cache. A new URL is a useful diagnostic, but it does not prove that a platform will refresh an old URL immediately.
The image works locally but not for crawlers
Verify the deployed URL is reachable without authentication, returns the correct content type, and is not blocked by network policy. Inspect the production response rather than relying only on a browser preview.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If you need a reliable screenshot of a rendered page, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Free tools Windows power users keep installed
One-click scans. No signup required.
For a one-call capture, see the ScreenshotNeo API documentation:
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also offers an MCP server so Claude, Cursor, or another MCP client can call take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Does Next.js require a separate metadata package for Open Graph images?
No. App Router file conventions and ImageResponse from next/og provide the documented framework integration.
Can one page have both a static and generated Open Graph image?
Use one applicable convention for a route and verify segment precedence. A deeper route image is selected over a higher-level image.
Are 1200 by 630 pixels mandatory?
No. They are the dimensions shown in the official generated-image example and documented defaults for the referenced API, not a universal requirement imposed by every platform.
Frequently Asked Questions
Can I reuse the same generated image for Twitter cards?
Use the separate `twitter-image` file convention when you need Twitter-specific metadata; its documented file-size limit differs from `opengraph-image`.
Does deploying on Vercel make social previews refresh immediately?
No hosting provider can guarantee another service’s crawler or cache behavior. Validate the public image response and account for the platform’s own refresh policy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

