Back to Blog
May 4, 20258 min read

How I Built My Blog Section – Next.js Blog with CMS & SEO

Next.jsBlog CMSSEOVercel BlobTypeScriptWebentwicklung

Why a Blog?

There are many good reasons to integrate a blog into a portfolio. The most important: SEO. Static portfolio pages struggle to rank for long-tail keywords. Blog posts, however, can be precisely optimized for search queries that potential clients actually type.

A blog also demonstrates expertise – *you don't just know how to build something, you can explain why.*

The Requirements

I had clear criteria for my blog section:

  • No ongoing costs – No expensive CMS subscription
  • Bilingual – German and English in parallel
  • SEO-optimized – Server-Side Rendering, structured data, dynamic metadata
  • Admin panel – Create new posts without a deployment
  • Premium design – Matching the rest of the portfolio

Technical Architecture

Next.js App Router with Server Components

The blog uses exclusively Next.js App Router and Server Components for maximum SEO performance. This means:

  • Every blog post is server-side rendered
  • Metadata is generated dynamically via generateMetadata()
  • Google immediately sees the full HTML content – no JavaScript rendering required

```typescript

// Dynamic SEO metadata per blog post

export async function generateMetadata({ params }) {

const post = getPostBySlug(params.slug)

return {

title: post.title.en,

description: post.description.en,

openGraph: { ... },

}

}

`

Bilingual Content

Each post contains title, description, and content as an object with de and en keys:

```typescript

interface BlogPost {

slug: string

title: { de: string; en: string }

description: { de: string; en: string }

content: { de: string; en: string }

date: string

tags: string[]

readTime: number

}

`

Language switching uses a LanguageContext – no routing change, no reload, just state.

The CMS: Vercel Blob

Instead of an expensive headless CMS (Sanity, Contentful, etc.), I use Vercel Blob as a free data store. Blog posts are saved as JSON files.

The architecture:

`

data/blog-posts.ts ← Static posts (existing)

+

Vercel Blob (*.json) ← New posts from admin panel

getAllPosts() ← Merges both sources

`

The Admin Panel

At /admin/blog there's a complete blog management interface:

  • Post list with all entries (static + CMS)
  • New post with live markdown editor for DE + EN
  • Edit existing CMS posts
  • Delete with confirmation
  • Password protection via Next.js Proxy and cookie auth

SEO: Structured Data (JSON-LD)

For each blog post, an Article JSON-LD Schema is automatically generated:

```json

{

"@type": "Article",

"headline": "Post title",

"datePublished": "2025-05-04",

"author": { "@type": "Person", "name": "Andrei P." },

"publisher": { "@type": "Organization", "name": "APsolution" }

}

`

Google can use this to display Rich Results in search – a direct SEO advantage.

XML Sitemap

All blog posts automatically appear in the dynamic sitemap.xml that Next.js generates via app/sitemap.ts. New CMS posts appear in the sitemap immediately.

Performance & Core Web Vitals

The design accounts for all three Core Web Vitals:

  • LCP (Largest Contentful Paint): Hero image loaded with priority
  • INP (Interaction to Next Paint): No heavy client-side processes
  • CLS (Cumulative Layout Shift): Image dimensions always explicitly specified

Lessons Learned

  1. Server Components first – Use SSR whenever possible, Client Components only where needed
  2. Plan bilinguality from day one – Adding it later is expensive
  3. Avoid CMS complexity – For a portfolio, Vercel Blob is completely sufficient
  4. SEO is not an add-on – It must be built into the architecture

Conclusion

The blog section consists of ~15 new files and is fully integrated into the existing portfolio infrastructure. No external services, no subscription costs, maximum control.

Want a system like this for your own website? Email me: apsolution.at@gmail.com