Skip to content
OGCheckup

Preview doctorog:title

Why your link preview shows the wrong title

The card shows no headline, your site name instead of the page's, or a title cut off halfway. All three come down to one tag.

Shows on
Check your page

Updated

The 2-minute fix

Paste this into the <head> of each page, with that page’s own headline. Keep it under about 60 characters, without your site name at the end.

<meta property="og:title" content="How we cut our build time in half">

Why og:title matters

The title is the bold line of a link preview, and the first thing people read. Facebook, LinkedIn, WhatsApp, Discord and Slack take it from og:title. X uses twitter:title if you set one, and og:title otherwise, so one tag covers every platform.

Without og:title, many platforms fall back to the page’s <title>. X doesn’t, so a page with neither og:title nor twitter:title gets an X card with no title. And the fallback is often written for search results, with a suffix like “| Example Blog” that wastes space in a card.

How long it should be

Aim for 40 to 60 characters, with the important words first. Platforms cut long titles at different points:

  • X cuts after about 70 characters.
  • Facebook and WhatsApp after about 100, though on phones a title often wraps to two lines and is cut sooner.
  • LinkedIn shows up to about 150, and Discord up to 256.

The checker flags titles over 90 characters, which most platforms cut off. The cheat sheet lists every platform’s limits.

Common reasons the title is wrong

There’s no og:title
The card falls back to <title>, or shows nothing on X. Add og:title to every page.
Every page has the same title
A site-wide layout often sets og:title once, so every shared page shows your home page’s title. Set it per page from that page’s own heading.
The tag appears twice
A theme and an SEO plugin can both write og:title, and platforms don’t agree on which one wins. Keep exactly one. The checker reports duplicate tags.
Special characters come out garbled
Write characters such as & and quotes as HTML entities (&amp;, &quot;), and serve the page as UTF-8. Framework metadata APIs do the escaping for you.
The title is added by JavaScript
Crawlers don’t run JavaScript, so a title set in the browser is never seen. See why your app’s links show a blank preview.

Code for your framework

These set og:title together with the other tags a preview needs. In each one, use the page’s own title, not a fixed string.

HTML inside <head>
<title>How we cut our build time in half</title>
<meta name="description" content="What we changed in our CI pipeline, and what it saved.">
<link rel="canonical" href="https://example.com/blog/faster-builds">

<!-- Open Graph -->
<meta property="og:title" content="How we cut our build time in half">
<meta property="og:description" content="What we changed in our CI pipeline, and what it saved.">
<meta property="og:url" content="https://example.com/blog/faster-builds">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Example Blog">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://example.com/images/faster-builds.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="A chart of build times dropping from 12 to 6 minutes">

<!-- X (Twitter). X falls back to the og: tags for title, description and image. -->
<meta name="twitter:card" content="summary_large_image">
Next.js (App Router) in page.tsx or layout.tsx
import type { Metadata } from "next";

// app/<route>/page.tsx (or layout.tsx)
export async function generateMetadata(): Promise<Metadata> {
  return {
    title: "How we cut our build time in half",
    description: "What we changed in our CI pipeline, and what it saved.",
    alternates: { canonical: "https://example.com/blog/faster-builds" },
    openGraph: {
      title: "How we cut our build time in half",
      description: "What we changed in our CI pipeline, and what it saved.",
      url: "https://example.com/blog/faster-builds",
      type: "article",
      siteName: "Example Blog",
      locale: "en_US",
      images: [
        {
          url: "https://example.com/images/faster-builds.jpg",
          width: 1200,
          height: 630,
          alt: "A chart of build times dropping from 12 to 6 minutes",
        },
      ],
    },
    twitter: {
      card: "summary_large_image",
    },
  };
}
Nuxt in the page component
<script setup lang="ts">
useSeoMeta({
  title: "How we cut our build time in half",
  description: "What we changed in our CI pipeline, and what it saved.",
  ogTitle: "How we cut our build time in half",
  ogDescription: "What we changed in our CI pipeline, and what it saved.",
  ogUrl: "https://example.com/blog/faster-builds",
  ogType: "article",
  ogSiteName: "Example Blog",
  ogLocale: "en_US",
  ogImage: "https://example.com/images/faster-builds.jpg",
  ogImageWidth: 1200,
  ogImageHeight: 630,
  ogImageAlt: "A chart of build times dropping from 12 to 6 minutes",
  twitterCard: "summary_large_image",
});

useHead({
  link: [{ rel: "canonical", href: "https://example.com/blog/faster-builds" }],
});
</script>
React Router / Remix in the route module
// In a route module (React Router v7 framework mode or Remix v2).
export function meta() {
  return [
    { title: "How we cut our build time in half" },
    { name: "description", content: "What we changed in our CI pipeline, and what it saved." },
    { tagName: "link", rel: "canonical", href: "https://example.com/blog/faster-builds" },
    { property: "og:title", content: "How we cut our build time in half" },
    { property: "og:description", content: "What we changed in our CI pipeline, and what it saved." },
    { property: "og:url", content: "https://example.com/blog/faster-builds" },
    { property: "og:type", content: "article" },
    { property: "og:site_name", content: "Example Blog" },
    { property: "og:locale", content: "en_US" },
    { property: "og:image", content: "https://example.com/images/faster-builds.jpg" },
    { property: "og:image:width", content: "1200" },
    { property: "og:image:height", content: "630" },
    { property: "og:image:alt", content: "A chart of build times dropping from 12 to 6 minutes" },
    { name: "twitter:card", content: "summary_large_image" },
  ];
}

In Next.js, an openGraph object without a title copies the page’s full <title>, including a suffix from title.template such as "%s | Example Blog". Set openGraph.title yourself to keep the suffix out of the card. A page without its own openGraph inherits the one from its layout. If that one sets a title, every page shares it.

After you fix it

Platforms cache previews, so the old title can stay for a while. Ask Facebook to re-fetch the page in the Facebook Sharing Debugger (opens in a new tab) and LinkedIn in the LinkedIn Post Inspector (opens in a new tab). On X, share the URL with a harmless query string such as ?v=2.