blogstart
Version:
Automatically setup SEO-optimized blog routes for Next.js projects
122 lines (104 loc) • 3.55 kB
JSX
import { notFound } from "next/navigation";
import Link from "next/link";
// Fetch blog posts from your JSON endpoint
async function getBlogPosts() {
try {
// For local development and production
const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "";
const response = await fetch(`${baseUrl}/sample-blog-posts.json`, {
next: { revalidate: 3600 }, // Revalidate every hour
});
if (!response.ok) {
throw new Error("Failed to fetch blog posts");
}
const data = await response.json();
return data.posts || [];
} catch (error) {
console.error("Error fetching blog posts:", error);
return [];
}
}
export async function generateMetadata({ params }) {
const { slug } = await params;
const posts = await getBlogPosts();
const post = posts.find((p) => p.slug === slug);
if (!post) return {};
return {
title: `${post.title}`,
description: post.description,
};
}
export async function generateStaticParams() {
const posts = await getBlogPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPage({ params }) {
const { slug } = await params;
const posts = await getBlogPosts();
const post = posts.find((p) => p.slug === slug);
if (!post) return notFound();
const currentDate = new Date().toISOString();
const estimatedReadingTime =
Math.ceil(
((post.content.intro?.length || 0) +
(post.content.platformImpact?.length || 0) +
(post.content.howWeHelp?.length ||
post.content.howLayzrHelps?.length ||
0)) /
1000
) || 5;
return (
<div className="bg-white">
<main className="max-w-3xl mx-auto px-6 py-20 bg-white">
<h1 className="text-3xl font-bold">{post.title}</h1>
<p className="mt-4 text-zinc-600 text-lg">{post.content.intro}</p>
<section className="mt-10">
<h2 className="text-2xl font-semibold">
Why This Matters for {post.platform}
</h2>
<p className="mt-2 text-zinc-600">{post.content.platformImpact}</p>
</section>
<section className="mt-10">
<h2 className="text-2xl font-semibold">
Common Mistakes on {post.platform}
</h2>
<ul className="list-disc list-inside mt-2 text-zinc-600 space-y-2">
{post.content.commonMistakes.map((item, idx) => (
<li key={idx}>{item}</li>
))}
</ul>
</section>
<section className="mt-10">
<h2 className="text-2xl font-semibold">How We Help</h2>
<p className="mt-2 text-zinc-600">
{post.content.howWeHelp || post.content.howLayzrHelps}
</p>
</section>
<a
href="/"
className="inline-block mt-10 px-6 py-3 bg-black text-white rounded-xl hover:bg-zinc-800 transition"
>
{post.cta}
</a>
{(post.relatedPages?.length || 0) > 0 && (
<section className="mt-12">
<h2 className="text-xl font-semibold mb-4">Related Pages</h2>
<ul className="list-disc list-inside space-y-2 text-blue-600">
{post.relatedPages?.map((p) => (
<li key={p.slug}>
<Link href={`/blog/${p.slug}`}>{p.title}</Link>
</li>
))}
</ul>
</section>
)}
</main>
</div>
);
}