DEV Community

Alex MacArthur
Alex MacArthur

Posted on • Originally published at macarthur.me on

2

Why I Moved from Notion to Ghost for My Headless CMS

I've been dogfooding JamComments as I iterate on it, and as a part of that effort, I've committed to blogging a little more regularly. It works out because I like doing both of those things.

Along the way, I also decided to move away from using flat Markdown files for blog posts. I wanted something a little friendlier for "on-the-go" writing, and it was becoming a little too cumbersome to manage the publishing process via Git repository (there are solutions to this, but I'm not totally sold on any of them).

I like the authoring experience of Notion, and was intrigued by its relatively new API, so I took the plunge and decided to upgrade this statically rendered site with Notion as its CMS.

That transition was fine until until things started to hurt. Quickly enough, I began to realize Notion might not be the best platform on which to further scale this sort of content. In the end, I chose to make another switch – this time, to the open source version of Ghost. I'll break it all down a bit in the form of several numbered lists.

Issues w/ Notion

Notion's writing experience is very good, but in terms of using it as a headless CMS, there were a few key things I didn't want to tolerate anymore.

#1. Build times were slow. Last I checked, Notion's API doesn't support fetching all of a page's content at once. Instead, you're required to fetch a page's individual blocks and stitch the content together. That makes for a lot of API requests and a lengthy build, compromising not only the development experience, but also the update process. Making the slightest tweak to a single post meant waiting for another wrinkle on my face to surface and a gray hair to sprout.

#2. API limits were frustrating. The fact that API limits exist isn't a problem, but it sure becomes a bear when you need to spend a lot of requests building a single page (see the previous point). I'd commonly find myself blocked from making more requests (especially during development), and would need to wait a few minutes to get going again. Technically, there are ways around it (the response will let you know how long you need to wait via Retry-After header), but doing so only further exacerbates the "slow build time" problem.

To avoid hitting limits in local development, I set up a local caching mechanism using lowdb. It did the job, but it always felt like more complexity than I should need to manage for a personal blog, and I occassionally became annoyed when I needed to remember to blow away that cache when I wanted to see freshest content.

#3. It's too easy to publish. There's no dedicated "save" or "publish" feature in Notion. When you make a change, it's "stuck," and totally eligible to be taken up with the next build of my site. I was constantly worried about accidentally tweaking the content, and later discovering I had butt-typed and sent an unwelcome change to my production site.

#4. Handling images was too complicated. I was initially excited about the fact that Notion's image URLs expire after some time and how I'd need to engineer some solution to make them more/less permanent. It was fun -- I used Cloudflare Workers and R2 to pull it off and even wrote a post about it. But in hindsight, it became another level of complexity I didn't really want to maintain or deal with later on. Plus, it contributed to the long build times.

#5. I want webhooks. At the time of writing, Notion doesn't offer any webhooks that fire when content is updated. I would have loved to use this for rebuilding my site whenever a post was added, updated, or deleted. Instead, I needed to rely on manually triggering a build myself as needed; or waiting for the cron job I have set up to rebuild after some time. I've seen people get creative to solve for this, but I wasn't willing to take on that challenge.

Searching for a Headless Option

I'm cheap and I really like my static site, so I knew moving from Notion would mean looking for good, affordable (free), headless options. WordPress was a top contender. I have a solid amount of experience with the platform and felt I could knock it out pretty quickly. But a few things deterred me from taking that path. Another list:

#1. Self-hosted WordPress doesn't support Markdown out of the box. I'd need to research, vet, and install a separate plugin. I just didn't wanna deal.

#2. There's no built-in membership/newsletter support. I'd need to yet again go through the plugin vetting process, or else stick with something like Mailchimp, which I didn't love.

#3. I'm a little burned out on WordPress. There's nothing very tangible to this one. I just wanted something that I felt was a little more... focused, cruft-less, and removed from that ecosystem.

Then Ghost Came Along

I've known about Ghost for several years and while I've always liked the vibe it emits, I had never tried it. But when I did, I was impressed. List time:

#1. The writing experience is slickkk. This is the thing that first got me hooked. The UI is modern, simple, clean, and intuitive. It supports Markdown-like syntax out of the box too, which made it real easy to get acclimated. I also got all of the blog goodies I previously took for granted (drafts, tags, featured images, etc.) – things I needed to cobble together with various fields in Notion. And there's also a dedicated "publish" button baked into the platform.

#2. The REST API is fast and free of limits. It also comes with JavaScript SDKs for both pulling content and managing admin-related things. I had no issues and was blown away by the results, especially coming from what I had become used to (no more stiching pages together with multipme requests!). Look at the build time difference my Notion-powered and Ghost-powered sites:

Keep in mind that the "slow" approach involved both pulling content from the API, as well as the fancy image handling I was doing with Cloudflare. Still, that's like nine times faster, and I get to shed a whole lot of complexity along the way. It was an easy sell.

#3. It comes with built-in newsletter/subscription support. To be honest, managing a newsletter and subscribers wasn’t even on my radar when I initially started looking at Ghost. But then I started to explore the feature and discover just how respected it is in the content world (many have migrated to it as an alternative to Substack). Mailchimp had always felt a little heavy-handed for me, and I liked the idea of managing both my content and subscribers in a single place. And once I toyed around with membership features of the REST API, it became pretty clear this was a serious alternative to what I had been using.

#4. It's got webhooks. And good ones! I can easily fine-tune when a new build is triggered, which is nice because it means it's not triggered whenever a meaningless event takes place (like when a draft is updated, for example). Here's a view into the hooks I have set up as of writing:

In all, it takes away all of the complexities I was getting sick of dealing with, and leaves me with features I actually care about.

Here's a First

Usually, soon after making a pretty large shift like this, I start feeling a hint of malaise over how it's all put together. It's the curse of any developer – soon enough, it becomes incessant enough to knock it all down and rebuild it with a different foundation.

But this time feels different. My current stack provides pretty much everything I've consistently wanted. Astro gives me a performant static site with a great developer experience (I've written about this). Plausible gives lightweight, privacy-minded analytics (this too). And now Ghost gives me a hassle-free CMS that I really enjoy using (refresh the page to see what I've written about this).

I think I'll be leaving things where they're at for a while.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more