Building a Shareable Astro Blog Engine

We kept rebuilding the same blog features - content collections, search, dark mode. Same patterns every time. So we extracted it into a package.

We’ve started too many blogs from scratch.

Every time: set up content collections, add search, wire up dark mode, build a related content system. Same work, slightly different implementation, each project learning from the last.

After the third or fourth time, we finally did what we should’ve done from the start - pulled it all into a reusable package.

The problem with copy-paste

Our previous approach was copying files between projects. Works fine until you fix a bug in one place and forget about the copies. Or you add a feature to the newest blog and the older ones stay frozen in time.

This site had evolved through three major iterations. Good patterns emerged. But they were trapped in one codebase.

Why Astro

Astro is a web framework built for content. It ships zero JavaScript by default - your pages are static HTML until you need interactivity. Perfect for blogs where most pages are just text and images.

What makes it work for us:

  • Content collections - Type-safe markdown with schema validation. Define your frontmatter structure once, get autocomplete and error checking everywhere.
  • Islands architecture - Interactive components only hydrate where needed. The search modal loads JavaScript; the rest of the page doesn’t.
  • Build-time rendering - Pages generate at build time, not on each request. Fast, cheap to host, works on any static host.

The blog engine builds on these foundations. It provides the content schemas, the layouts, the components - all the pieces you’d write yourself but don’t want to repeat. Astro handles the rest.

What’s in the package

Click to enlarge
flowchart LR
  A[Markdown files] --> B[Blog Engine]
  B --> C[Posts]
  B --> D[Projects]
  B --> E[Labels]
  B --> F[Search]
  B --> G[RSS]

Posts and Projects

Two content types. Posts are your typical blog entries - title, date, excerpt, tags. Projects are for showcasing work with status tracking (active, completed, archived) and links to repos or live demos.

Both use Astro’s content collections. Write markdown, add frontmatter, done.

Labels

Tags connect content across your site. Tag a post with “typescript” and it shows up on the typescript label page alongside any projects with the same tag.

The label cloud on the homepage shows what you write about most. Click any label, see everything tagged with it.

Client-side search powered by Pagefind. Indexes at build time, loads a tiny search UI. Hit Cmd+K (or Ctrl+K) anywhere on the site.

No server required. Works offline once the page loads.

Dark mode

Detects system preference on first load. Toggle to override. Persists in localStorage.

We spent too much time getting this right - no flash of wrong theme on load, smooth transitions, colors that actually work in both modes. More on that below.

Reading time

Estimated reading time on posts based on word count. Simple, but readers appreciate knowing what they’re getting into.

At the bottom of each post, related content based on shared tags. The matching weights recent content and tag overlap. Not perfect, but better than random.

RSS

Auto-generated feed at /rss.xml. Works out of the box.

Diagrams

MDX support means you can drop in components. We include Mermaid for flowcharts and diagrams - like the one above. Click to expand.

Getting started

Drop it into an Astro project:

import blogEngine from '@ritmus/astro-blog-engine';

export default defineConfig({
  integrations: [
    blogEngine({
      site: {
        title: 'Your Blog',
        description: 'What it's about'
      }
    })
  ]
});

Add markdown files in src/content/posts/ and you’ve got a blog.

The hard part: theming

Colors were tricky. We wanted simple configuration - pick three colors and you’re done. But those colors need to work in light mode, dark mode, and with sufficient contrast for accessibility.

OKLCH color space helped. It separates lightness from hue, so you can derive light and dark variants from a single base color automatically. Still tweaking the approach, but it’s close.

What’s next

Open sourcing is on the roadmap. We want proper docs before putting it out there. But the core is stable - this site runs on it.