Building a Local-First CMS for a Static Site
Over the past few weeks, I’ve been building a lightweight, custom admin interface for my 11ty website — a small-but-mighty setup that gives me the flexibility of a CMS without losing the simplicity and performance of static content.
Why 11ty
I love 11ty for how it keeps everything close to the metal. It doesn’t abstract away HTML or force me into a framework — it lets me write Markdown, sprinkle in Nunjucks or shortcodes, and build exactly what I need. But once a site grows past a handful of posts, managing Markdown by hand can get old.
That’s where this project began: how can I keep the static workflow but add the ease of editing and publishing?
The Admin Interface
I wrote a local admin app using Node, Express, and React. It runs at http://localhost:4001, connects to my local content directory, and provides a clean Markdown editor for managing blog entries and pages.
Each post is a Markdown file inside a content folder, so the system remains fully Git-based and deploys statically — no database, no external CMS. With the admin panel, I can preview, edit, and publish without leaving the browser.
Current features:
- Markdown editing with a live preview pane.
- AI integration for generating summaries, intros, or titles (powered by OpenAI).
- Deploy button that triggers a push or build to the live server.
- Notification system that can post updates to Telegram or other channels after a deploy.
- File watcher and autosave for a smooth editing flow.
How It Works
- App: React UI talks to a local Express API.
- Content: Markdown files with Front Matter in a content/ directory.
- Parsing: gray-matter for Front Matter; remark or markdown-it for preview.
- Build: optional 11ty build-on-save locally, or build-on-deploy in CI.
- Deploy: git push to main or trigger CI; optionally hit a webhook.
- Notifications: Telegram bot or other hooks configured via environment variables.
Example content structure:
content/
  posts/
    2025-01-03-local-first-cms.md
  pages/
    about.md
assets/
  images/
Why Local-First
Running the admin panel locally keeps me in control of the content pipeline. There’s no hosted dependency, no login, and no vendor lock-in — just local files, Git, and a small API for talking to the live site when needed.
It feels like the best of both worlds:
- Static simplicity for the web.
- A personal CMS for the creator.
What’s Next
- 
Image uploads and automatic resizing. - Drag-and-drop to assets/images.
- Generate responsive sizes (e.g., 320/640/960/1280) and WebP/AVIF via sharp.
- Insert Markdown or a shortcode that references the responsive set.
- Optional alt-text helper and focal-point cropping.
 
- 
Front Matter editor enhancements. - Typed fields with validation (title, date, tags, draft, description, canonical, coverImage).
- Schema-driven forms (e.g., JSON/YAML schema or Zod) to keep content consistent.
- Presets and templates per collection (posts, pages, notes) with default values.
- Computed fields for slug/permalink based on title/date, with a preview before saving.
- Tag/category autocomplete sourced from existing content.
- Draft/published workflow with visual status and scheduled publish dates.
- SEO preview (title length, description length, social image).
- Required-field checks before enabling “Publish.”
 
- 
Optional cloud deploys via webhook or SSH. - Webhook: POST to CI to run the 11ty build and deploy.
- SSH: run a remote script for pull/build/restart on a server.
- Branch- or environment-based targets (staging vs. production).
 
- 
More AI-powered tools. - Outline/slug/title suggestions tuned to site voice.
- Excerpt and meta description generation with length checks.
- Grammar/style passes that respect Front Matter tone/reading-level settings.
- Alt-text suggestions for images with human-in-the-loop approval.
 
Example Front Matter
Here’s the shape I’m standardizing on for posts:
---
title: Building a Local-First CMS for a Static Site
date: 2025-01-03
draft: false
tags: [engineering, cms, 11ty]
description: A lightweight admin interface for 11ty with a local-first workflow.
canonical: https://example.com/blog/local-first-cms
coverImage:
  src: /assets/images/local-first-cover.jpg
  alt: A screenshot of the admin interface in edit mode
  credit: ""
---
Notes and Trade-offs
- Security: the admin runs locally; don’t expose it to the internet. Keep API keys in env vars.
- Collaboration: great for solo use; for teams, rely on Git branches/PRs or add lightweight auth.
- Portability: everything stays in files and Git, so the site remains portable and future-proof.
If you’re happy in Markdown but want less friction, a local-first CMS adds just enough ergonomics without giving up the benefits of static sites.