DEV Community

Daniel Hall
Daniel Hall

Posted on

I built a visual blog editor because existing CMS options frustrated me

## The Problem

I've been running a static blog with Astro for a while. The writing experience was... not great. My workflow looked like this:

  1. Open VS Code
  2. Create a new markdown file
  3. Copy frontmatter from another post because I can never remember the format
  4. Write the post
  5. Realize I messed up the date format
  6. Fix it
  7. Drag image into the folder, figure out the relative path
  8. Commit and push

Not the end of the world, but enough friction that I kept procrastinating on writing.

I tried Keystatic as a headless CMS. Spent a weekend configuring it, and honestly it felt like overkill. The UI was clunky and I still had to deal with a bunch of config files.

So I did what any reasonable developer would do - I spent way more time building my own solution.

## What I Built

A web-based editor that connects to your GitHub repo directly. You login with GitHub OAuth, pick your blog repo, and start writing. Posts save as regular markdown files, so you're not locked into anything.

Here's what it looks like:

editor screenshot

The core features:

Just write - A clean BlockNote-based editor with live preview. No distractions.

Images that just work - Drag an image into the editor, it uploads to your repo and inserts the markdown. Also auto-optimizes images so you don't accidentally commit a 5MB photo.

Frontmatter without the YAML headaches - Fill out a form for title, description, date, tags. It generates the frontmatter for you.

Draft recovery - This one saved me multiple times. The editor auto-saves to localStorage every few seconds. Accidentally close the tab? Your draft is still there when you come back.

Dark mode - Because of course.

## Tech Stack

I wanted to keep it simple:

Backend: FastAPI (Python)

  • GitHub OAuth flow
  • PyGithub for repo interactions
  • Pillow for image processing

Frontend: Next.js 14

  • App Router
  • shadcn/ui for components
  • TailwindCSS
  • Zustand for state
  • React Query for data fetching

The frontend proxies API requests to the backend, so in production you can deploy them separately (I use Vercel + Railway).

## How It Works

The flow is pretty straightforward:

  1. User logs in with GitHub OAuth
  2. Backend stores the access token in session
  3. User selects which repo contains their blog
  4. Frontend fetches the list of markdown files from the repo
  5. When editing, we parse the frontmatter and content separately
  6. On save, we commit directly to the repo via GitHub API

The "database" is literally just your GitHub repo. No extra infrastructure needed.

## Self-Hosting

If you want to run your own instance:


bash
  # Backend
  cd blog-editor-api
  pip install -r requirements.txt
  # Set up .env with your GitHub OAuth credentials
  uvicorn app.main:app --reload --port 8000

  # Frontend
  cd blog-editor-web
  npm install
  npm run dev

  You'll need to create a GitHub OAuth app and configure the callback URL. Full instructions in the repo README.

  Try It

  - Live demo: https://editor.deploy.re
  - Source code: https://github.com/DanielHallx/static-blog-editor

  It works with any static site generator that uses markdown files - Astro, Hugo, Jekyll, Eleventy, Gatsby, etc.

  ---
  This started as a tool for myself but figured others might find it useful. If you have feedback or feature ideas, let me know in the comments or open an issue on GitHub.

  And yes, I wrote this post using the editor itself.
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.