From a4eca69fac550400941169fa12cb208c9f6d10db Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Fri, 30 Oct 2026 22:07:33 +0000 Subject: [PATCH] Add SEO and social metadata to share pages --- app/(marketing)/blog/[slug]/page.tsx | 29 ++++---- app/(marketing)/recent/page.tsx | 27 +++----- app/c/[project]/[slug]/page.tsx | 15 ++-- app/c/[project]/page.tsx | 11 +-- app/r/[token]/page.tsx | 28 +++----- app/surveys/[slug]/invite/opengraph-image.tsx | 42 ++++++++++++ app/surveys/[slug]/invite/page.tsx | 68 ++++++++++++++++--- app/surveys/r/[token]/page.tsx | 9 ++- lib/seo/seo-aeo-meta.ts | 62 +++++++++++++++++ 9 files changed, 213 insertions(+), 78 deletions(-) create mode 100644 app/surveys/[slug]/invite/opengraph-image.tsx create mode 100644 lib/seo/seo-aeo-meta.ts diff --git a/app/(marketing)/blog/[slug]/page.tsx b/app/(marketing)/blog/[slug]/page.tsx index 50690de8..ec10ee89 100644 --- a/app/(marketing)/blog/[slug]/page.tsx +++ b/app/(marketing)/blog/[slug]/page.tsx @@ -5,6 +5,7 @@ import { stripDeadLinks } from "@/lib/dead-links"; import { stripTrailingAd } from "@/lib/blog/split-html"; import { AdUnit } from "@/components/ads/ad-unit"; import { env } from "@/lib/env"; +import { createSeoAeoMetadata, serializeJsonLd } from "@/lib/seo/seo-aeo-meta"; // The article body's typography. const PROSE = @@ -25,21 +26,23 @@ export async function generateMetadata({ }) { const { slug } = await params; const post = await findAnyPost(slug); + const url = `${env.siteUrl.replace(/\/$/, "")}/blog/${slug}`; if (!post) { - return { title: "Post not found", alternates: { canonical: `/blog/${slug}` } }; + return createSeoAeoMetadata({ + title: "Post not found", + description: "This CrawlProof post is no longer available.", + url, + robots: { index: false, follow: false }, + }); } - return { + return createSeoAeoMetadata({ title: post.title, - description: post.excerpt ?? undefined, - alternates: { canonical: `/blog/${slug}` }, - openGraph: { - type: "article", - url: `/blog/${slug}`, - title: post.title, - description: post.excerpt ?? undefined, - images: post.image_url ? [{ url: post.image_url }] : undefined, - }, - }; + description: post.excerpt || post.title, + url, + type: "article", + socialCard: post.image_url ? "summary_large_image" : "summary", + ...(post.image_url ? { image: post.image_url, imageAlt: `${post.title} featured image` } : {}), + }); } export default async function BlogPost({ @@ -69,7 +72,7 @@ export default async function BlogPost({