How to Make an Open Graph Image Render Correctly in Dark Mode
Dark mode does not select a different Open Graph image. Set the right metadata and design one share image that stays clear across light and dark interfaces.
Short answer: Dark mode does not make a social preview choose a different Open Graph image. The Open Graph protocol provides og:image as the image URL representing a page; it does not define a light/dark image selector. The HTML color-scheme metadata describes which color schemes a document supports or prefers, and does not change the image URL a preview crawler reads. To make a preview look good in either interface, publish one clear, well-separated image and verify it on the platform where it will appear.
This distinction matters whether the card looks wrong against a dark app background or you expect a page’s dark-mode setting to swap the preview image. Fix the metadata if the wrong asset is selected; adjust the artwork if the right asset disappears against the surrounding interface.
1. How Open Graph and dark mode relate
An Open Graph object has a required og:image property containing the image URL that represents it. The protocol also defines optional image metadata such as secure URL, MIME type, dimensions, and alt text. It does not define a viewer color-scheme field or a dark-mode image variant. See the Open Graph protocol.
The color-scheme CSS property and corresponding HTML metadata tell user agents which schemes a document can support or prefers. That is separate from Open Graph metadata. Adding color-scheme: dark, a dark theme, or a prefers-color-scheme media query does not instruct a social crawler to select another og:image. See MDN’s color-scheme reference.
In practice, the displayed card is controlled by the destination service’s preview implementation. A robust image should remain legible against more than one surrounding surface. This is design guidance inferred from the fixed image URL model and the variation in preview displays, not a protocol guarantee that every platform renders images identically.
2. Design one image for light and dark surfaces
Start with one representative image rather than maintaining separate light and dark files that the destination may never select. Make the subject distinct from its background, preserve a clear margin or boundary around the composition, and avoid making a white edge blend into a light interface or a black edge disappear into a dark one. Check the artwork at reduced sizes as well as full size.
- Use sufficient contrast between the main subject and the image background.
- Give the composition breathing room so cropping or scaling does not remove its defining detail.
- Avoid relying on the outermost pixels to communicate where the image ends.
- Keep essential wording out of the image where possible. Apple notes that Messages previews can appear at varying sizes and advises avoiding text in preview images; use page metadata for the title and description instead. See Apple’s rich links guidance.
- Use a relevant, representative image and avoid extreme aspect ratios. Google’s recommendations concern image previews in Search and should not be treated as universal social-network rules. See Google Search Central’s image guidance.
There is no universal dimension or automatic dark-mode transformation established by these sources. Pick dimensions appropriate to the destination and verify its current guidance. Next.js uses 1200 × 630 as an example in its Open Graph image documentation; that is a framework example, not a cross-platform guarantee.
3. Add correct Open Graph metadata to plain HTML
Put the metadata in the final document’s <head>. Use absolute, publicly accessible URLs for the canonical page and image. The following is a complete minimal example; replace the example domain and asset path with your deployed values.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Guide to Share Images</title>
<link rel="canonical" href="https://example.com/guides/share-images">
<meta property="og:title" content="A Guide to Share Images">
<meta property="og:type" content="article">
<meta property="og:url" content="https://example.com/guides/share-images">
<meta property="og:image" content="https://example.com/images/share-images.png">
<meta property="og:image:alt" content="An illustrated guide to creating clear share images">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="color-scheme" content="light dark">
<style>
:root { color-scheme: light dark; }
body { font-family: system-ui, sans-serif; }
</style>
</head>
<body>
<main><h1>A Guide to Share Images</h1></main>
</body>
</html>
The color-scheme declaration above is included only to show that page theme support and share-image selection are separate. Remove it if the page does not support both schemes. The dimensions and MIME type describe the asset and should match the actual file; they do not guarantee a particular preview size.
Metadata checklist
- Set
og:title,og:type,og:url, andog:image. - Make
og:imagean absolute URL to the intended image, not a relative path or a mode-dependent page route. - Add descriptive
og:image:alt. If you provide image type and dimensions, make sure they match the file. - Inspect the deployed HTML response. A tag present in a client-side template but absent from the crawler-visible response will not help that crawler.
- Open the image URL directly and confirm it serves the expected asset to the destination platform.
4. Generate metadata with Next.js App Router
Next.js supports an opengraph-image file convention in a route segment and code-generated Open Graph images. The framework adds the appropriate metadata to the document head. Consult the current Next.js Open Graph image documentation for supported formats, file-size constraints, and current behavior, since framework details can change.
Static image convention
For a fixed image, place an image file in the route segment. For example:
app/
guides/
share-images/
page.tsx
opengraph-image.png
Next.js uses that file convention to generate the route’s Open Graph image metadata. Deploy the route, then view-source or fetch the rendered page to confirm the emitted og:image points to the expected generated asset.
Code-generated image
For a generated image, create app/guides/share-images/opengraph-image.tsx:
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = 'An illustrated guide to creating clear share images'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default function Image() {
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 64,
background: '#172033',
color: '#ffffff',
border: '24px solid #6ee7c5',
fontSize: 54,
fontWeight: 700,
}}
>
Share images that stay clear in every interface
</div>
),
size,
)
}
This example deliberately uses a contrasting frame so the graphic has a visible boundary on both light and dark surrounding surfaces. That is an illustrative design choice, not a required Next.js style or platform rule. The sample’s 1200 × 630 dimensions follow the framework documentation example. Verify the current Next.js constraints before relying on this implementation in production.
5. Verify what the destination actually sees
- Fetch the deployed page and inspect the returned HTML head. Confirm there is one intended
og:imagevalue and that it is the absolute URL you expect. - Open that image URL directly. Confirm it resolves to the correct image rather than an HTML error page, login screen, or outdated asset.
- Review the image against light and dark sample backgrounds, at full size and at the smaller size likely to appear in a card.
- Use the destination platform’s current preview or debugging tool, if it provides one. Preview selection and caching behavior vary by service.
- After changing the metadata or image, account for a previously cached preview. Use the platform’s documented refresh mechanism where available; there is no single cache-refresh procedure established for every service.
Apple Messages can show previews at varying sizes. Google’s image-preview selection is automated for Google Search and draws on multiple sources. That Search behavior does not establish how social networks choose their previews. Do not assume that a browser screenshot, page theme, or one platform’s preview exactly predicts another platform’s result.
6. Choosing between one image and separate variants
A single resilient image is usually the simplest implementation because the protocol supplies an image URL and the reviewed sources do not establish that social platforms select a mode-specific URL from the viewer’s preference. Separate images can still make sense when you control distinct distribution contexts and can explicitly publish a different URL for each context. Compare the options by whether the destination consumes the variant, the maintenance cost of keeping assets aligned, and whether each image remains clear at the sizes actually displayed.
| Approach | Use when | Trade-off |
|---|---|---|
| One resilient image | The same page is shared across interfaces and the platform reads a single og:image. |
Requires a composition that works on multiple surrounding surfaces. |
| Distinct image URLs | You control separate pages or publishing contexts that explicitly use different metadata. | More assets to maintain; a viewer’s dark-mode preference still does not make a crawler choose a variant. |
7. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| The preview uses the wrong image | The deployed head has a stale, duplicate, relative, or unexpected og:image. |
Inspect the final HTML response, remove conflicting tags, and set one correct absolute URL. |
| The image is missing | The URL is wrong, inaccessible to the crawler, or returns something other than the intended image. | Open the URL directly, verify the response and asset, and consult the destination platform’s current crawler requirements. |
| The image looks good in light mode but disappears in dark mode | Its background or edge blends into the preview surface, or its subject has too little separation. | Add visual separation and margins; check both light and dark sample surfaces and reduced-size rendering. |
Changing color-scheme had no effect |
That metadata controls document scheme support or preference, not Open Graph image selection. | Set the intended og:image explicitly and redesign the asset to work across surfaces. |
| The source template is correct, but the preview is not | The rendered response may omit the tags, or the platform may have cached an earlier preview. | Inspect deployed HTML and use the platform’s current preview refresh or debugging mechanism if available. |
| Next.js generates an unexpected image URL | The file convention may be in a different route segment, or the deployed output differs from the source tree. | Check the route placement and generated metadata in the deployed HTML; consult the current Next.js convention docs. |
| Words in the image become unreadable | The preview is scaled down or shown at a varying size. | Move essential wording into metadata and keep the artwork understandable without small text. |
8. Performance, reliability, and maintenance
- Keep metadata available in the rendered head. This makes the intended share URL straightforward to inspect and avoids depending on a theme change to update it.
- Use a stable image URL. If replacing the file at the same URL, a destination may continue to show a cached preview. A new asset URL can help distinguish a changed file, but refresh behavior is platform-specific.
- Keep generated image work predictable. For code-generated assets, follow the framework’s current runtime, format, and size constraints. Test the deployed output, not only the source component.
- Do not infer universal dimensions from one example. The Open Graph protocol permits dimension metadata, while platform and framework recommendations vary. Check current guidance for the destination you care about.
- Control asset maintenance cost. One well-designed image avoids keeping light and dark variants synchronized when the distribution platform does not expose a mode-specific selection mechanism.
Or skip the browser setup
If you need a rendered screenshot of the page to inspect its appearance, ScreenshotNeo is a website screenshot API and MCP server. A screenshot can help you review the page itself in a chosen viewport; it does not replace checking the destination platform’s actual share preview. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/guides/share-images -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/guides/share-images"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/guides/share-images',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the shot was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Can I set a dark-mode-specific og:image?
The reviewed Open Graph protocol does not define a dark-mode selector. A destination could provide its own behavior, but do not rely on that without documentation from that service.
Does color-scheme: light dark change the share card?
No. It communicates page scheme support or preference to user agents; it does not choose an alternate Open Graph URL.
Is 1200 × 630 required everywhere?
No universal dimension is established by the sources here. It is an example used in Next.js documentation. Check current destination guidance for the surfaces you target.
Can a screenshot confirm the social preview?
It can show how your page renders in a browser-like capture, but the destination’s own preview tool is the relevant check for its card, cropping, and cached content.


