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
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