From fa6cd08f9fe4d5f8b1d923763f3df2e262b27b2f Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Mon, 4 May 2026 00:01:22 -0400 Subject: [PATCH] SEO: apex canonical on project pages, add BreadcrumbList + richer Service schema, image schema --- src/app/projects/[slug]/page.tsx | 75 ++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/app/projects/[slug]/page.tsx b/src/app/projects/[slug]/page.tsx index dccdcab..2cf823d 100755 --- a/src/app/projects/[slug]/page.tsx +++ b/src/app/projects/[slug]/page.tsx @@ -4,6 +4,8 @@ import Image from "next/image"; import { ArrowLeft, ArrowRight, ExternalLink } from "lucide-react"; import { projects, getProjectBySlug } from "@/data/projects"; +const SITE_URL = "https://wilddragon.net"; + export function generateStaticParams() { return projects.map((project) => ({ slug: project.slug, @@ -15,8 +17,11 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str const project = getProjectBySlug(slug); if (!project) return { title: "Project Not Found" }; + const url = `${SITE_URL}/projects/${slug}`; + const ogImage = project.thumbnail.startsWith("http") ? project.thumbnail : `${SITE_URL}${project.thumbnail}`; + return { - title: `${project.client} | Zachary Gaetano — Broadcast Systems Integration`, + title: `${project.client} — ${project.category} | Broadcast Facility Case Study`, description: project.summary, keywords: [ ...project.technologies, @@ -27,24 +32,25 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str "Wild Dragon", "Washington DC", "broadcast facility design", + "case study", ], alternates: { - canonical: `https://www.wilddragon.net/projects/${slug}`, + canonical: url, }, openGraph: { - title: `${project.client} | Zachary Gaetano`, + title: `${project.client} — ${project.category} | Zachary Gaetano`, description: project.summary, - url: `https://www.wilddragon.net/projects/${slug}`, + url, siteName: "Wild Dragon", - type: "website", + type: "article", locale: "en_US", - images: [{ url: project.thumbnail, width: 1200, height: 630, alt: `${project.client} — ${project.category}` }], + images: [{ url: ogImage, width: 1200, height: 630, alt: `${project.client} — ${project.category} broadcast facility` }], }, twitter: { card: "summary_large_image", - title: `${project.client} | Zachary Gaetano`, + title: `${project.client} — ${project.category}`, description: project.summary, - images: [project.thumbnail], + images: [ogImage], }, }; } @@ -57,14 +63,45 @@ export default async function ProjectPage({ params }: { params: Promise<{ slug: notFound(); } + const url = `${SITE_URL}/projects/${slug}`; + const imageUrl = project.thumbnail.startsWith("http") ? project.thumbnail : `${SITE_URL}${project.thumbnail}`; + const structuredData = { "@context": "https://schema.org", - "@type": "Service", - name: project.title, - description: project.summary, - serviceType: project.category, - provider: { "@id": "https://www.wilddragon.net/#organization" }, - areaServed: "United States", + "@graph": [ + { + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "Home", item: `${SITE_URL}/` }, + { "@type": "ListItem", position: 2, name: "Projects", item: `${SITE_URL}/#projects` }, + { "@type": "ListItem", position: 3, name: project.client, item: url }, + ], + }, + { + "@type": "Service", + "@id": `${url}#service`, + name: project.title, + description: project.summary, + serviceType: project.category, + category: project.category, + provider: { "@id": `${SITE_URL}/#organization` }, + areaServed: "United States", + image: imageUrl, + url, + }, + { + "@type": "CreativeWork", + "@id": `${url}#case-study`, + name: `${project.client} — ${project.category}`, + headline: project.title, + description: project.summary, + about: project.technologies, + author: { "@id": `${SITE_URL}/#person` }, + publisher: { "@id": `${SITE_URL}/#organization` }, + image: imageUrl, + url, + }, + ], }; const currentIndex = projects.findIndex((p) => p.slug === project.slug); @@ -76,13 +113,13 @@ export default async function ProjectPage({ params }: { params: Promise<{ slug: