Journal · Engineering

Building a Custom CMS with Next.js and Firebase

How I gave this portfolio its own admin panel: server-rendered content for SEO, and a simple Firebase-backed editor for projects and posts.

Building a Custom CMS with Next.js and Firebase

I wanted to be able to add projects and write posts without redeploying my site every time. A full headless CMS felt like too much for a personal portfolio, so I built a small one with the tools I already use: Next.js and Firebase.

The shape of it

The data lives in two Firestore collections, projects and posts. Each document has a status of draft or published, a slug, and the usual fields. That single status flag does a lot of work: only published documents ever reach the public site.

The public pages are React Server Components that read Firestore on the server. That matters, because it means the content is in the HTML the moment the page loads, which is exactly what search engines want. Reads are cached and revalidated on a short interval, so updates show up without a rebuild.

Auth without the overhead

Writing is gated by Firebase Authentication. There is a single admin account, and the /admin routes are wrapped in a guard that redirects anyone who is not signed in. Firestore security rules enforce the same thing on the server side: anyone can read published content, but only a signed-in user can write.

The editor itself is plain React. Posts are written in Markdown and rendered to sanitized HTML at request time, so the database stays clean and the styling stays consistent.

Lessons

A few things I would tell myself at the start:

  • Keep the public read path and the admin write path separate. They have different needs.
  • Sanitize Markdown before you render it, even when you are the only author.
  • Let the data layer fail gracefully. If Firebase is not configured yet, the site should still build and render empty, not crash.

If you want to see the result, every project and post on this site goes through that exact pipeline. It is not much code, and it turns a static portfolio into something I can actually keep up to date.

← back to portfolio