All Blogs

Building the Blog Builder

Blog Image

For the past few months, I've been making a Notion inspired dashboard as a content management system to create blogs. Yes, I know about MDX and Medium, but I want to create a space to share my dumb ideas on my own website in the style I want and to be able to make changes instantly. Here's a blog about how the blog is built.


"Software developers like to solve problems. If there are no problems available, they will create their own problems."

Sun Tzu, Circa 2001

The project employs Drizzle as the ORM of choice to bridge the gap between the database and the application. PostgreSQL was chosen as the database because they have a free tier on Vercel for its compatibility with Drizzle and Typescript. Below is the database schema for the blogs table:

The blog content data is typed in the codebase instead of the database level as a list of BlogElement elements for future flexibility. The BlogElementTypes type is a union type defined by its type, data, and etc objects, where data defines the essential fields for an element while etc contains secondary optional data I may want to support later on.

Since the dashboard is hosted online, an authentication layer needs to be added to web requests. Authjs and Tanstack Query are used for auth and client-side state management in the builder, where CRUD operations are handled with Route Handlers. Nextjs server actions and server components are kind of a pain to use with client interactions and mutations, so the dashboard is made up of mostly client components.

Blogs are immutable outside of the builder, meaning that server actions, server components, and caching are suitable in optimizing SEO and application performance on published blog pages. Blogs are fetched with the getBlog server action and are cached with their url in their cache tag for cache invalidations. The server action is called in the server rendered components to generate metadata and is streamed into the rendered UI.

Blog likes are handled by Redis as a key-value store. Likes are backed up to the database daily with cron jobs just to be safe. I would cache the blog content as well with Redis but I'm scared of going over the free tier on Vercel.

The UI feature implementations are a lot less exciting to share, as it's mostly just following the docs from libraries that are used. Since all our attention spans are busted, here's a few GIFs of some of the features.


"Hours of trial and error will save you minutes of reading documentation."

Olivia Rodrigo

Blog Image
Implemented with dndkit, the drag & drop library to reorder elements
Blog Image
Implemented with UploadThing, a wrapper around AWS S3 to upload & host files
Blog Image
Element CRUD operations & type changing done with keyboard event listeners

There's still a lot of features that I want to implement in the builder but aren't worth the time at the moment I need to start job searching. Here's a few that I want to add in the future:

Video & Audio Media Elements

IFrame Embeds

Nested Layouts

👋 Thanks for stopping by!