Write the title, description and image once and get the full head, the Next.js metadata object, and a preview of the card people will actually see.
<title>Free developer tools</title> <meta name="description" content="Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser." /> <link rel="canonical" href="https://ui.beste.co/tools" /> <meta property="og:type" content="website" /> <meta property="og:site_name" content="Beste UI" /> <meta property="og:title" content="Free developer tools" /> <meta property="og:description" content="Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser." /> <meta property="og:url" content="https://ui.beste.co/tools" /> <meta property="og:image" content="https://images.unsplash.com/photo-1780024269547-074e54a2a91a?q=80&w=2070&auto=format&fit=crop" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="Free developer tools" /> <meta name="twitter:description" content="Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser." /> <meta name="twitter:image" content="https://images.unsplash.com/photo-1780024269547-074e54a2a91a?q=80&w=2070&auto=format&fit=crop" />
export const metadata: Metadata = {
title: "Free developer tools",
description: "Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser.",
alternates: { canonical: "https://ui.beste.co/tools" },
openGraph: {
title: "Free developer tools",
description: "Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser.",
url: "https://ui.beste.co/tools",
siteName: "Beste UI",
images: ["https://images.unsplash.com/photo-1780024269547-074e54a2a91a?q=80&w=2070&auto=format&fit=crop"],
},
twitter: {
card: "summary_large_image",
title: "Free developer tools",
description: "Tailwind and shadcn helpers, CSS generators, color maths and converters. No signup, everything runs in your browser.",
images: ["https://images.unsplash.com/photo-1780024269547-074e54a2a91a?q=80&w=2070&auto=format&fit=crop"],
},
};
Around 60 characters for a title and 155 for a description. Past that they are cut off in results, and the sentence you cared about is the half that disappears.
Twitter falls back to Open Graph for most fields, so the only one that truly earns its place is twitter:card. The rest are written here because other readers do consult them.
1200 by 630, and served from an absolute URL. A relative path works on your own page and breaks in every crawler.
Because every platform caches what it scraped. Facebook and LinkedIn both have a post inspector that refetches on demand, and Slack re-reads after a while. Adding a query string to the image URL forces a new fetch when the file changed but the URL did not.
Not at first. One good image beats a hundred bad ones, and a dynamic route that draws the title onto a template is the usual middle ground. What matters is that the URL is absolute and the file is under about 5MB, or the crawler gives up.