T

TechIdea

Ecosystem

Back to nextjs Projects
Beginner Level

Next.js Personal Blog

Create a beautiful Personal Blog in Next.js App Router demonstrating static rendering, dynamic routing, metadata configuration, and markdown rendering architecture.

The Problem

Create a beautiful Personal Blog in Next.js App Router demonstrating static rendering, dynamic routing, metadata configuration, and markdown rendering architecture.

Real-World Use Case

Create a beautiful Personal Blog in Next.js App Router demonstrating static rendering, dynamic routing, metadata configuration, and markdown rendering architecture.

Technology Stack

Understanding of React components

Prerequisite

Familiarity with Next.js App Router structure

Prerequisite

Basic understanding of SEO metadata

Prerequisite

Architecture & Design

Folder Structure

personal_blog/
├── app/
│   ├── blog/
│   │   └── [slug]/
│   │       └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
└── package.json

Step-by-Step Implementation

1

Initialize Next.js App Router project.

### Step 1: Project Setup Initialize Next.js project with TypeScript and App Router.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

2

Create blog post data list array.

### Step 2: Data & Static Routing Define blog posts array and generateStaticParams.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

3

Implement blog overview page rendering list of article cards.

### Step 3: Page Layouts Build home page and dynamic article detail page.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

4

Implement blog/[slug]/page.tsx dynamic route handler.

### Step 4: SEO & Edge Cases Ensure notFound() triggers for unknown slugs.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

5

Implement generateStaticParams for build-time pre-rendering.

### Step 4: SEO & Edge Cases Ensure notFound() triggers for unknown slugs.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

6

Configure generateMetadata for dynamic Open Graph tags.

### Step 4: SEO & Edge Cases Ensure notFound() triggers for unknown slugs.

nextjs
// Sample implementation of app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';

const POSTS = [
  { slug: 'first-post', title: 'Getting Started with Next.js 15', date: '2026-05-01', content: 'Next.js App Router brings server components and advanced caching to frontend development.' },
  { slug: 'seo-guide', title: 'Next.js SEO Best Practices', date: '2026-05-05', content: 'Metadata configuration and static rendering are essential for ranking on Google.' },
];

export function generateStaticParams() {
  return POSTS.map(post => ({ slug: post.slug }));
}

export function generateMetadata({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) return { title: 'Not Found' };
  return { title: `${post.title} | Personal Blog`, description: post.content };
}

export default function BlogPostPage({ params }) {
  const post = POSTS.find(p => p.slug === params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-2xl mx-auto my-12 p-8 bg-white rounded-3xl shadow-sm border border-slate-200">
      <h1 className="text-3xl font-bold text-slate-900 mb-2">{post.title}</h1>
      <time className="text-xs text-slate-500 mb-6 block">{post.date}</time>
      <div className="prose text-sm text-slate-700 leading-relaxed pt-6 border-t border-slate-100">
        <p>{post.content}</p>
      </div>
    </article>
  );
}

Code Explanation

Implementation step

Common Errors

generateStaticParams mismatch.

Ensure param keys exactly match folder name [slug].

Security & Performance

Navigate to /blog/first-post and verify metadata title.

Test unknown slug to ensure 404 page renders.


Add MDX parsing capabilities.

Add dark mode toggling.

Implement reading time estimation.

Interview Questions

Q: Why use generateStaticParams?

A: It allows Next.js to pre-render dynamic routes into static HTML at build time for maximum speed.

Growth Newsletter

Get practical AI tools, SEO tips, and growth guides weekly.

Join creators, students, and businesses scaling with TechIdea.