import { postBySlugQuery, postSlugsQuery, urlFor } from '@/lib/sanity' import { sanityFetch } from '@/lib/sanityFetch' import { PortableTextRenderer } from '@/components/PortableTextRenderer' import Image from 'next/image' import Link from 'next/link' import { notFound } from 'next/navigation' import type { Metadata } from 'next' export const revalidate = 60 type Params = { slug: string } type Post = { _id: string title: string slug: { current: string } publishedAt: string excerpt: string | null mainImage: { asset: object; alt?: string } | null body: unknown[] seoTitle: string | null seoDescription: string | null author: { name: string; image?: object; bio?: string } | null } export async function generateStaticParams() { try { const slugs: { slug: string }[] = await sanityFetch<{ slug: string }[]>(postSlugsQuery) return slugs.map((s) => ({ slug: s.slug })) } catch (err) { console.error('[Guides] generateStaticParams fetch failed:', err) return [] } } export async function generateMetadata({ params }: { params: Promise }): Promise { const { slug } = await params let post: Post | null = null try { post = await sanityFetch(postBySlugQuery, { slug }) } catch (err) { console.error('[Guides] generateMetadata fetch failed:', err) } if (!post) return {} return { title: post.seoTitle ?? post.title, description: post.seoDescription ?? post.excerpt ?? undefined, openGraph: { title: post.seoTitle ?? post.title, description: post.seoDescription ?? post.excerpt ?? undefined, images: post.mainImage?.asset ? [{ url: urlFor(post.mainImage).width(1200).height(630).url() }] : [], }, } } function formatDate(iso: string) { return new Date(iso).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', }) } export default async function PostPage({ params }: { params: Promise }) { const { slug } = await params let post: Post | null = null try { post = await sanityFetch(postBySlugQuery, { slug }) } catch (err) { console.error('[Guides] PostPage fetch failed:', err) } if (!post) notFound() return (
{/* Breadcrumb */} {/* Header */}
{post.author?.name && {post.author.name}} {post.author?.name && ยท}

{post.title}

{post.excerpt && (

{post.excerpt}

)}
{/* Hero image */} {post.mainImage?.asset && (
{post.mainImage.alt
)} {/* Body */} {post.body && (
)} {/* CTA footer */}

Ready to put this into practice?

Use the Battl Builder to price out your parts and track your total.

Start Your Build โ†’
{/* Back link */}
โ† Back to all guides
) }