OG images
How to Build Rich Link Previews Like Slack and iMessage
July 29, 2026 · 6 min read · Grabbit Team

When you paste a link into Slack, iMessage, or Discord, it usually expands into a card with a title, a short description, and an image. That card is a rich link preview, and when yours shows up as a bare URL instead, the cause is almost always the same across every platform: the page is missing the Open Graph tags those apps read, or its og:image points at something they cannot fetch. This guide covers how the previews work, the exact tags to add, and how to give the card a real image of your page instead of a generic logo.
How a rich link preview works
Slack, iMessage, Discord, and the social platforms do not screenshot your page on demand or guess at its content. When a URL is posted, the app fetches your page's raw HTML and looks for Open Graph meta tags in the <head>. Four tags drive the entire card:
<meta property="og:title" content="Your Page Title" />
<meta property="og:description" content="A one-line summary of the page." />
<meta property="og:image" content="https://yourdomain.com/og.png" />
<meta property="og:url" content="https://yourdomain.com/page" />
og:title and og:description become the text. og:image becomes the picture. og:url is the canonical link the card points at. If a page has no Open Graph tags at all, the app falls back to the bare URL with no card. That is what a "broken" preview almost always is: missing or unreachable tags, not a bug in Slack or iMessage.
Two details trip people up on every platform:
- The fetcher reads the raw HTML, not the rendered page. It does not run your JavaScript. If your meta tags are injected client-side (a single-page app that sets them after load), the fetcher never sees them. The tags must be in the HTML the server sends.
og:imagemust be an absolute, public URL. A relative path like/og.pngwill not resolve. Use the fullhttps://yourdomain.com/og.png, and make sure that URL is reachable without a login or a redirect the app cannot follow.
The tags every platform reads
The good news for anyone building link previews into an app: you do not write a different set of tags per platform. Open Graph is the shared standard, so one correct set of tags produces a rich card in Slack, iMessage, Discord, Facebook, and LinkedIn at once. Add one X-specific tag and you cover X (Twitter) too:
<meta property="og:title" content="Your Page Title" />
<meta property="og:description" content="A one-line summary of the page." />
<meta property="og:image" content="https://yourdomain.com/og.png" />
<meta property="og:url" content="https://yourdomain.com/page" />
<meta name="twitter:card" content="summary_large_image" />
twitter:card set to summary_large_image tells X to render the wide card instead of a small thumbnail; X falls back to your og:image for the picture, so you do not need a separate twitter:image. That is the whole tag set. The work that remains is producing the image, and making sure the tags actually reach the fetcher.
Making the preview image a real render of your page
The og:title and og:description are just strings. The og:image is the part that makes a preview feel rich, and it is also the part most people get wrong by shipping a single generic logo for every URL. For a marketing page, a docs page, or a product page, the best preview image is often a clean shot of the page itself.
You can produce that with a screenshot API: point it at the URL, get back a hosted image, and use that image as your og:image.
curl https://api.grabbit.live/v1/grabs \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com/og",
"width": 1200,
"height": 630,
"format": "png",
"delay_ms": 500
}'
The response includes a hosted image_url:
{
"id": "grb_01jx...",
"status": "done",
"image_url": "https://cdn.grabbit.live/grabs/grb_01jx....png",
"width": 1200,
"height": 630,
"format": "png",
"bytes": 71240,
"execution_ms": 1240
}
width accepts 320 to 1920 and height 240 to 1080, so the 1200 by 630 card is comfortably in range. delay_ms (0 to 10000) gives client-rendered content or web fonts a moment to settle before the capture fires. Drop the returned image_url into your tag:
<meta property="og:image" content="https://cdn.grabbit.live/grabs/grb_01jx....png" />
Capture once per unique page and cache the image_url alongside the page record rather than rendering on every request. A common pattern is to build a dedicated /og route with your normal components, capture that at 1200 by 630, and reuse the result everywhere. The same template-and-cache approach is covered in how to generate dynamic OG images from any URL, and the per-platform fix-it details are in how to make your Discord links unfurl.
Why 1200 by 630 covers every platform
Slack, iMessage, Discord, Facebook, X, and LinkedIn all render the same 1.91 to 1 aspect ratio as a large card, and 1200 by 630 pixels is the size that satisfies all of them. That is why one og:image fixes the preview everywhere at once: you are not producing a Slack image and an iMessage image and a Discord image, you are producing one Open Graph image that every reader parses.
Smaller images still work, but many apps fall back to a small thumbnail beside the text below roughly 300 pixels wide, which reads as a weaker preview. Stay at 1200 by 630 and the card is full-width on every platform.
Why your preview still is not showing
If the tags are present and the image URL works in a browser but a platform still shows nothing, work through these in order:
- The platform cached the old version. Slack, iMessage, Discord, and Facebook all cache the metadata they fetched the first time a URL was posted, so editing your tags will not update a preview that already rendered. Append a unique query string, such as
?nocache=1, and post the link again so the app treats it as a new URL and re-fetches. Facebook and X also offer manual re-scrape tools in their sharing debuggers. - The image is behind auth or a redirect. If
og:imagerequires a login, sits behind a paywall, or redirects in a way the fetcher will not follow, the card renders without an image. Test the raw image URL in a private browser window. - The tags are set by JavaScript. The fetchers read static HTML and do not execute scripts. Server-render the meta tags so they are in the initial response.
- The image is too small. Below roughly 300 pixels wide, many apps render a small thumbnail instead of a full card. Use 1200 by 630.
Re-fetch first, because a stale cache is the single most common reason a correct tag set still shows nothing.
The short version
A rich link preview is your page's Open Graph tags rendered as a card, and every platform reads the same four tags. Add og:title, og:description, og:image, and og:url server-side, plus twitter:card for X; make og:image an absolute, public, 1200 by 630 URL; and append ?nocache=1 to force a refresh after changes. If you want the image to be a real render of the page instead of a static logo, a screenshot API turns the live URL into the hosted image you point og:image at. New to the format? Start with what is an OG image.
FAQ
- How do rich link previews work?
- When you paste a URL into Slack, iMessage, Discord, or any social app, the app fetches the page's raw HTML and reads its Open Graph meta tags: og:title, og:description, og:image, and og:url. It builds the preview card from those four values. No app screenshots your page on the fly or runs your JavaScript; it reads the tags in the HTML the server sends. If a page has no Open Graph tags, the link shows up bare with no card.
- What tags do I need for a rich link preview?
- Four Open Graph tags in the page's <head>: og:title (the headline), og:description (a one-line summary), og:image (an absolute, public image URL), and og:url (the canonical link). Add twitter:card set to summary_large_image so X renders the wide card too. These must be server-rendered, because the apps that build previews read the raw HTML and do not execute your JavaScript.
- Why is my link preview not showing?
- The common causes are: the page has no og:image, or it points at a relative or broken URL; the image sits behind a login or a redirect the app cannot follow; the tags are injected client-side by JavaScript that the fetcher never runs; or the platform cached an earlier version of the page. Confirm the four tags are in the served HTML (view source or curl the URL), make sure og:image is a public absolute URL, then force a re-fetch.
- What size should a link preview image be?
- 1200 by 630 pixels, the 1.91 to 1 ratio that Slack, iMessage, Discord, Facebook, X, and LinkedIn all render as a large card. One image at that size covers every platform. Smaller images still work but many apps show them as a small thumbnail beside the text instead of a full-width card.
- How do I make the preview image a screenshot of my page?
- Point a screenshot API at the page (or a dedicated /og route) at 1200 by 630, get back a hosted image URL, and use that as your og:image. Capture once per unique page and cache the returned image_url alongside the page record rather than rendering on every request. The same tag then drives the preview on every platform.
Capture any website with one API call
Get a free test key and capture your first screenshot in two minutes.
Written by
Grabbit Team
Screenshots as a service
The team behind Grabbit, the screenshot API for developers and AI agents. We write about web capture, rendering, and automating screenshots at scale.
Keep reading
What Is an OG Image? Why Your Shared Links Look Broken
An OG image is the preview image that appears when a link is shared on social media. Learn what it is, why it breaks, and how to automate one per page.
Jun 13, 2026 · 5 min read
How to Generate Dynamic OG Images from Any URL
Generate a unique Open Graph image for every page by pointing a screenshot API at your HTML template. One template, zero design work, scales to any number of pages.
Jun 12, 2026 · 4 min read

Discord Link Previews: How to Make Your Links Unfurl
Why your links show up bare in Discord and how to fix it: add the right Open Graph tags, point og:image at a real 1200x630 render, and force Discord to re-fetch the metadata.
Jun 21, 2026 · 6 min read