← All posts

Building a news platform with MDX and Next.js App Router

How I rebuilt tyflos.gr — a Greek cultural radio and editorial site — using MDX files, Next.js generateStaticParams, and zero database.

When a Greek radio station asked me to rebuild their editorial platform, the brief had two hard constraints: no CMS subscription, and page loads under 200ms.

MDX files with Next.js static generation hit both targets.

The content model

Every article is an .mdx file with frontmatter:

title: "Μίκης Θεοδωράκης — 100 Χρόνια"
category: "Μουσική"
subcategory: "Μουσικοί"
date: "2026-02-09"
author: "Λάμπης Καϊτεφτζίδης"
readTime: 6
excerpt: "Εκατό χρόνια από τη γέννηση..."
coverImage: "/images/theodorakis.jpg"
featured: true

gray-matter parses the frontmatter at build time. generateStaticParams pre-renders every article page. Result: a static site with full editorial control, zero database, and Vercel's CDN serving every page.

Category pages without a database

The category filter is just an array .filter() over the article metadata:

export async function generateStaticParams() {
  const categories = [...new Set(getAllArticles().map(a => a.category))]
  return categories.map(category => ({ category }))
}

That's it. No SQL, no API call. Build time scales linearly with article count — 200 articles builds in under 3 seconds.

The trade-off

Content updates require a re-deploy. For a radio station publishing 3–5 articles per week, that's fine — a Vercel webhook on GitHub push makes it invisible. For a publication pushing breaking news every hour, you'd want ISR or a proper CMS.

Phase 2 of this project will wire in Sanity for the editorial team and keep MDX for static pages (About, Categories). The MDX content functions are already abstracted behind a getArticles() interface — swapping the data source is a single file change.

Accessibility

The site is named after the Greek word for "blind" — WCAG AA was non-negotiable. The main callouts:

Accessibility isn't a feature — it's table stakes.