Files
Greenlens/greenlns-landing/app/blog/[slug]/page.tsx
2026-08-05 19:39:22 +02:00

60 lines
1.7 KiB
TypeScript

import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import { blogPosts } from '@/lib/blogPosts'
import { BlogPostTemplate } from '@/components/blog/BlogPostTemplate'
import { getHreflangAlternates } from '@/lib/localeRoutes'
interface Props {
params: Promise<{ slug: string }>
}
export async function generateStaticParams() {
return Object.keys(blogPosts).map((slug) => ({ slug }))
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const post = blogPosts[slug]
if (!post) return {}
const languages = getHreflangAlternates(post.canonical)
return {
title: post.metaTitle,
description: post.metaDescription,
// Selbstreferenzierender Canonical. Wichtig für die Syndication: Wenn der
// Beitrag auf Medium, DEV.to oder LinkedIn zweitveröffentlicht wird, muss
// dort ein rel=canonical auf genau diese URL zeigen.
alternates: {
canonical: post.canonical,
...(languages && { languages }),
},
openGraph: {
type: 'article',
title: post.metaTitle,
description: post.metaDescription,
url: post.canonical,
publishedTime: post.publishedAtIso,
modifiedTime: post.updatedAtIso || post.publishedAtIso,
authors: [post.author.name],
images: [{ url: post.heroImage, width: 1200, height: 630, alt: post.heroImageAlt }],
},
twitter: {
card: 'summary_large_image',
title: post.metaTitle,
description: post.metaDescription,
images: [post.heroImage],
},
}
}
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params
const post = blogPosts[slug]
if (!post) {
notFound()
}
return <BlogPostTemplate post={post} />
}