Open Graph and Twitter Cards, link previews for Telegram, WhatsApp, Facebook (2026)

open graph5 min readJuly 25, 2026

Author: DevStudio.it

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
WhatsApp og:title, og:description, og:image aggressive cache
Telegram og:* sometimes also twitter:*
LinkedIn 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 &lt;head&gt;.

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
Facebook Sharing Debugger → Scrape Again
LinkedIn Post Inspector → inspect
X Card Validator
WhatsApp 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:

  1. Store ogImageUrl on the record
  2. Or generate PNG via ImageResponse with title on brand background

Generated cards look consistent but need per-platform testing-some crawlers have scrape timeouts.

Pre-publish checklist

  • Absolute og:image HTTPS, 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_image for 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:

  1. Background, brand color or a photo with a strong overlay (text must stay readable on mobile)
  2. Logo, small, in a safe-zone corner; do not let the logo own 40% of the frame
  3. Title on the image, shorter than HTML og:title (about 6–8 words); the HTML title can be longer
  4. No tiny UI, do not paste a full dashboard screenshot; pick one motif
  5. 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 &lt;title&gt;. Force rescrape in Inspector; ensure SSR serves current HTML.

Want correct social cards on your site?

Related posts

Trust Signals on Corporate Websites, Conversion and E-E-A-T in 2026
6 min read
FAQPage Schema and Rich Results, When Google Shows FAQs (2026)
5 min read
robots.txt in Next.js App Router, Crawl Budget and Disallow Pitfalls (2026)
5 min read

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.

Like how we think? Let's build something together.

Start project configuration