feat: initialize landing page with dynamic competitor comparison routes and structured SEO metadata

This commit is contained in:
Timo Knuth
2026-04-10 16:18:01 +02:00
parent 8a9533c520
commit e8d013d99a
14 changed files with 1277 additions and 58 deletions

View File

@@ -0,0 +1,70 @@
import type { Metadata } from 'next'
import { notFound } from 'next/navigation'
import ComparisonPage from '@/components/ComparisonPage'
import { competitorOrder, getCompetitorBySlug, getPeerCompetitors } from '@/lib/competitors'
import { siteConfig } from '@/lib/site'
type ComparisonRouteProps = {
params: Promise<{ competitor: string }>
}
export function generateStaticParams() {
return competitorOrder.map((competitor) => ({ competitor }))
}
export async function generateMetadata({ params }: ComparisonRouteProps): Promise<Metadata> {
const { competitor } = await params
const profile = getCompetitorBySlug(competitor)
if (!profile) {
return {}
}
const path = `/vs/${profile.slug}`
return {
title: profile.metaTitle,
description: profile.metaDescription,
alternates: {
canonical: path,
},
keywords: [
`${siteConfig.name.toLowerCase()} vs ${profile.name.toLowerCase()}`,
`${profile.name.toLowerCase()} alternative`,
'plant emergency app',
'plant care app comparison',
'plant diagnosis app',
],
openGraph: {
title: profile.metaTitle,
description: profile.metaDescription,
url: `${siteConfig.domain}${path}`,
type: 'website',
images: [
{
url: '/og-image.png',
width: 1200,
height: 630,
alt: `${profile.metaTitle} comparison page`,
},
],
},
twitter: {
card: 'summary_large_image',
title: profile.metaTitle,
description: profile.metaDescription,
images: ['/og-image.png'],
},
}
}
export default async function ComparisonRoute({ params }: ComparisonRouteProps) {
const { competitor } = await params
const profile = getCompetitorBySlug(competitor)
if (!profile) {
notFound()
}
return <ComparisonPage competitor={profile} peers={getPeerCompetitors(profile.slug)} />
}