Ahmad Alghawi
Building a Firestore-backed Markdown Blog

2026-04-30 · 2 min read

Building a Firestore-backed Markdown Blog

How I added a fully admin-driven blog to my React 19 portfolio using Firestore, react-markdown, and a custom prerender pipeline — no headless CMS required.

firebase · react · blog · seo

Why Firestore?

I already had Firestore powering projects, experience and the contact form on my portfolio. Adding a blog was a natural fit: one auth model, one set of security rules, one bill.

The data shape

A post is just a document in the posts collection:

interface Post {
  id: string;
  slug: string;
  title: string;
  excerpt: string;
  content: string;     // raw markdown
  tags: string[];
  published: boolean;
  featured: boolean;
  publishedAt?: string;
  updatedAt?: string;
}

Security rules

The single most important rule:

match /posts/{id} {
  allow read: if resource.data.published == true || isAdmin();
  allow write: if isAdmin();
}

This keeps drafts private without needing a separate collection.

Rendering

Public reads use react-markdown with remark-gfm, rehype-slug and rehype-autolink-headings so every <h2> and <h3> gets a stable id — which the table-of-contents component then observes with IntersectionObserver.

SEO

A small scripts/prerender.ts runs after vite build and emits one static HTML file per published post, with title, description, canonical, Open Graph and a BlogPosting JSON-LD block injected into <head>. Crawlers see fully-rendered HTML; users still get the SPA experience.

Takeaway

You don't need Contentful, Sanity or Notion-as-CMS to ship a blog. If you already have Firestore in your stack, a few hundred lines of code gets you all the way there.