TL;DR
When someone pastes a URL into Telegram, WhatsApp, Facebook, LinkedIn, or X, the app builds a card from Open Graph (og:*) and-for X-Twitter Cards. Without correct meta tags you get a random favicon, a truncated title, or no image. Card standard: 1200×630 (~1.91:1), absolute HTTPS URL, og:title, og:description, og:image, og:url, og:type. In Next.js App Router set this in generateMetadata / metadata. Deploy on DevStudioIT Cloud; dynamic OG for case studies via Branchly. Below: tag checklist, Next.js code, crawler cache pitfalls, and tests.
Who this is for
- Companies sharing blog posts and landings in social / chat
- Next.js App Router developers without WordPress SEO plugins
- Marketers seeing an “ugly preview” despite a polished site
- Multilingual teams (pl/en/de) needing per-locale title/description
Keywords (SEO)
open graph nextjs, twitter cards link preview, og:image 1200x630, facebook whatsapp telegram meta tags, social share card 2026, generateMetadata open graph
What the crawler reads (and what it ignores)
Messengers do not run your client JavaScript in the user’s browser-they HTTP GET and parse HTML (often early bytes / SSR). Therefore:
- Meta must be in the first HTML (Server Components / SSG), not only injected in
useEffect - The image must be publicly reachable (not behind auth, not
localhost) - Large JS-only SPAs without SSR = empty or wrong previews
| Platform | Primary tags | Notes |
|---|---|---|
| Facebook / Messenger | og:* |
Debugger forces rescrape |
og:title, og:description, og:image |
aggressive cache | |
| Telegram | og:* |
sometimes also twitter:* |
og:* |
Post Inspector | |
| X (Twitter) | twitter:card, twitter:image… |
summary_large_image |
Minimal tag set
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DevStudio.it" />
<meta property="og:locale" content="en_US" />
<meta property="og:url" content="https://devstudioit.com/en/blog/slug" />
<meta property="og:title" content="Card title, max ~60–70 chars" />
<meta property="og:description" content="1–2 sentences, ~110–160 chars" />
<meta property="og:image" content="https://devstudioit.com/og-share.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Image description" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Card title" />
<meta name="twitter:description" content="Description" />
<meta name="twitter:image" content="https://devstudioit.com/og-share.jpg" />For blog posts: og:type = article plus optional article:published_time, article:author.
Image spec 1200×630
| Parameter | Recommendation |
|---|---|
| Size | 1200×630 px (safe standard) |
| Ratio | ~1.91:1 |
| Format | JPG or PNG; WebP can fail on some crawlers |
| Weight | usually < 1 MB (better < 300 KB) |
| Safe zone | text/logo in the center ~80%, edges may crop |
| URL | absolute https://... |
Project default fallback: /og-share.jpg. Per-post override via frontmatter ogImage.
Do not upload a 4K full-page screenshot-crawlers rescale anyway; you only slow the scrape.
Next.js App Router, generateMetadata
import type { Metadata } from 'next';
const BASE = 'https://devstudioit.com';
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string; slug: string }>;
}): Promise<Metadata> {
const { locale, slug } = await params;
const post = await getPost(locale, slug); // markdown or Branchly
const image = post.ogImage?.startsWith('http')
? post.ogImage
: `${BASE}${post.ogImage ?? '/og-share.jpg'}`;
const url = `${BASE}/${locale}/blog/${slug}`;
return {
title: post.title,
description: post.description,
alternates: { canonical: url },
openGraph: {
type: 'article',
locale: locale === 'pl' ? 'pl_PL' : locale === 'de' ? 'de_DE' : 'en_US',
url,
siteName: 'DevStudio.it',
title: post.title,
description: post.description,
images: [{ url: image, width: 1200, height: 630, alt: post.title }],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
images: [image],
},
};
}Root layout can set defaults; pages override. Avoid two conflicting og:image tags in <head>.
Crawler cache, the #1 “bug report”
You changed the image, WhatsApp still shows the old one? That is platform cache, not your CDN.
| Platform | Tool / trick |
|---|---|
| Sharing Debugger → Scrape Again | |
| Post Inspector → inspect | |
| X | Card Validator |
often: append ?v=2 for testing; wait or try another device |
|
| Telegram | @webpagebot or re-paste after a delay |
On DevStudioIT Cloud, use long Cache-Control on OG images only when final-while iterating, version filenames (og-share-2026-07.jpg).
Dynamic cards (optional)
For case studies from CMS/DB (Branchly) you can:
- Store
ogImageUrlon the record - Or generate PNG via
ImageResponsewith title on brand background
Generated cards look consistent but need per-platform testing-some crawlers have scrape timeouts.
Pre-publish checklist
- Absolute
og:imageHTTPS, 200 OK - Dimensions match (width/height meta = real pixels)
- Title ≤ ~70 chars, description not mid-word truncated
-
og:url= locale canonical - Separate title/description for pl/en/de
-
twitter:card=summary_large_imagefor large image - Test FB Debugger + paste into Telegram/WhatsApp on a public staging URL
Typography and branding on a 1200×630 card
A social card is a mini-billboard: in half a second it must convey brand and topic. Practical rules:
- Background, brand color or a photo with a strong overlay (text must stay readable on mobile)
- Logo, small, in a safe-zone corner; do not let the logo own 40% of the frame
- Title on the image, shorter than HTML
og:title(about 6–8 words); the HTML title can be longer - No tiny UI, do not paste a full dashboard screenshot; pick one motif
- Locale consistency, separate PL/EN/DE art if the title is burned into the bitmap
For the DevStudio blog a template often suffices: gradient + title line + brand. Unique art per post can lift LinkedIn/FB CTR, but do not block publishing waiting on design-fallback /og-share.jpg beats a missing og:image.
FAQ
Is one site-wide og-share.jpg enough?
As a fallback-yes. For blog and key landings a unique image (title + branding) usually lifts feed CTR.
Can og:image be relative /og-share.jpg?
Next.js Metadata API often absolutizes, but crawlers are picky. Prefer full https://... URLs.
Open Graph vs JSON-LD, the same?
No. OG = social preview. JSON-LD = Google understanding (Organization, BlogPosting). You want both.
Why does LinkedIn show a different title than og:title?
Often its own cache or <title>. Force rescrape in Inspector; ensure SSR serves current HTML.
Want correct social cards on your site?
- Contact us, metadata, 1200×630 OG, and preview tests in Next.js
- Next.js SEO metadata App Router, title, description, hreflang
- JSON-LD Schema.org, structured data alongside OG
About the author
We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.
Recommended links
From theory to production — Branchly, our hosting stack and shipped work.
