Open graph images are the pictures that appear when a link is shared - in Slack, on LinkedIn, in iMessage, on X. Most sites use one static image for every page, which is why so many shared links look identical.
Dynamic open graph images generate per page or per user instead, pulling in real data: an article title, a user's name, a score, a product. They are more effective and not much harder, provided you know where the traps are.
First, a clarification that saves a lot of wasted effort
People often ask about open graph images "for email campaigns," and the two things are unrelated in a way that is worth being precise about.
Open graph images are for link previews. A crawler fetches your page, reads the og:image meta tag, and renders a preview card. This happens on social platforms and chat apps.
Email clients never read og:image. An image inside an email is a plain <img> tag pointing at an image URL. There is no meta tag involved and no crawler.
So if what you want is a personalized image inside an email, you do not need open graph at all - you need an image URL with merge tags, which is a different and simpler job.
Where the two genuinely meet is when an email contains a link that recipients then share - a referral link, a results page, a shared report. The email carries the link; the open graph image controls what the preview looks like when someone posts it somewhere. That is a real use case and worth building for.
When dynamic OG images are worth the effort
Not always. A static brand image is fine for most marketing pages.
They earn their place when the link itself is specific to something:
- Content pages at volume. Blog posts, docs, listings - a preview showing the actual title and author beats a generic logo on every one.
- User-generated or user-specific pages. A shared profile, a result, a public dashboard. The preview showing the person's own name or number is what makes it shareable.
- Referral and invite links. "Sarah invited you" performs differently from your logo.
- Anything with a number that moves. A leaderboard position, a count, a score.
The common thread is that somebody is sharing the link because of what is on that page specifically. If every page on your site is basically the same page, static is fine.
Building one
The mechanics are straightforward, because an open graph image is just an image at a URL. Anything that generates images from URL parameters will do it.
Build the design once with placeholders for the variable parts - title, name, number, whatever the page carries.
Generate the URL per page, passing that page's values as parameters:
https://media.okzest.com/img?c=YOUR-COMPANY-ID&i=YOUR-DESIGN-ID&title=Quarterly%20Report&name=Sarah%20Chen
Put it in the page head:
<meta property="og:image" content="https://media.okzest.com/img?c=YOUR-COMPANY-ID&i=YOUR-DESIGN-ID&title=Quarterly%20Report&name=Sarah%20Chen" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:title" content="Quarterly Report" />
<meta property="og:description" content="..." />
<meta name="twitter:card" content="summary_large_image" />
Declaring the width and height matters more than people expect - some platforms render the preview before they have finished fetching the image, and without dimensions they guess badly.
The five things that break it
This is where the actual difficulty lives. None of it is about generating the image.
1. Platforms cache aggressively, and they cache your mistakes.
Once Facebook or LinkedIn has scraped a URL, it holds that preview - often for a long time. Fixing the image does not fix the preview, because nothing re-fetches it.
Every major platform has a debugger that forces a refresh:
- Facebook - Sharing Debugger
- LinkedIn - Post Inspector
- X - Card Validator
Get the image right before the link circulates widely. Re-scraping one URL is easy; re-scraping a thousand is not.
2. Crawlers do not run JavaScript.
The og:image tag must be in the HTML the server returns. If your meta tags are injected client-side by a framework, crawlers see nothing. On a single-page app this means server-side rendering or pre-rendering for the routes that get shared.
3. The URL must be absolute, and publicly reachable.
/images/og.png will not work - it needs the full https://.... And the image cannot sit behind authentication, a staging password, or an IP allowlist. Crawlers arrive as anonymous visitors from unfamiliar addresses.
4. Parameters need encoding.
Titles contain spaces, ampersands, apostrophes and colons. Q3 Report: Sales & Growth has to become Q3%20Report%3A%20Sales%20%26%20Growth. An unencoded ampersand will silently truncate your parameters and produce an image missing half its content.
5. Slow generation can lose you the preview.
This is the one that catches people using on-demand generation. Crawlers give a URL a limited window and move on if it does not respond. If your image is generated fresh on every request and generation is slow, some scrapers will give up and show nothing.
The fix is caching. Serve generated images with cache headers so that the first request does the work and subsequent ones are served from cache:
Cache-Control: public, max-age=1800, s-maxage=1800
If a page is important and predictable, it is also worth requesting the image URL once yourself after publishing, so it is warm before anyone shares it.
Getting the dimensions right
1200 × 630 is the practical standard - a 1.91:1 ratio that all the major platforms handle. Design at that size.
Two things to build in:
Assume it will be cropped. Different platforms crop differently, and some render a square thumbnail. Keep anything essential well inside the middle.
Make the text bigger than feels right. Previews are frequently viewed at a few hundred pixels wide on a phone. Text that is comfortable in your design tool at full size can be unreadable in the actual preview. Zoom out to 30% and see whether it still reads.
Test before it goes anywhere
Three minutes, and it catches almost everything:
- Paste the URL into the Facebook Sharing Debugger and check the image renders
- Run it through LinkedIn's Post Inspector - LinkedIn is fussier than most
- Send the link to yourself in Slack and iMessage, which both render previews and behave differently
- Try a page with a long title and one with an apostrophe or ampersand
If it works in all four, it will work almost everywhere.
Frequently asked questions
How do I create dynamic open graph images?
Build one image design with placeholders, generate a URL per page with that page's values as parameters, and put the URL in the og:image meta tag in the server-rendered HTML. The image is produced when a crawler fetches it.
Do open graph images work in email?
No. Email clients do not read og:image - an image in an email is a plain <img> tag. Open graph controls what appears when a link is shared on social platforms or in chat apps. If you want a personalized image inside an email, you need an image URL with merge tags instead.
What size should an open graph image be? 1200 × 630 pixels, a 1.91:1 ratio. Declare the width and height in meta tags, keep essential content away from the edges because platforms crop differently, and use larger text than feels necessary.
Why isn't my open graph image updating? Because the platform cached the old one. Use the Facebook Sharing Debugger, LinkedIn Post Inspector, or X Card Validator to force a re-scrape. Changing the image alone will not refresh an existing preview.
Why is my open graph image not showing at all? Common causes: the URL is relative rather than absolute, the meta tag is injected by JavaScript so crawlers never see it, the image sits behind authentication, or unencoded parameters have broken the URL.
Can open graph images be generated on demand? Yes, but cache them. Crawlers allow a limited window before giving up, so an uncached image generated fresh on every request risks the preview failing. Cache headers solve it, and warming important URLs once after publishing helps.
Do I need a developer for this? Some. Building the image design is a visual task, but adding meta tags to the server-rendered HTML requires access to the templates. It is a small job, not a project.
What data can I put in a dynamic OG image? Anything the page knows - title, author, a user's name, a score, a count, a price. The practical limit is legibility rather than technology: one or two pieces of information read well at preview size, five do not.
Building them
The design is the small part. The care goes into caching, encoding, and testing across the platforms that will actually render it.
OKZest generates images from a URL with parameters, which is exactly the shape an og:image needs - one design, a URL per page, cached and served on request.
Create a free account, no credit card required.
Related reading: personalized images explained, adding logos and screenshots to images.