Notion as a CMS

I’ve been tinkering with Notion as a CMS for the last few days and decided it checked all the boxes for a simple site like my own. Below is a high level recap of some of the keys steps. But for more detail, you can check out the source for my site.

The Framework: NextJS

First of all, I chose NextJS for this because I’m familiar with it, it has a lot of options for switching between different types of rendering and React is something I’m comfortable with. But, really, this is workable in any static site generator.

The CMS: Notion API

Notion has a very clean integration API that is described well here:

Used in combination with the notion client for javascript/typescript along with some custom built renderers, I was able to easily ingest content from Notion and render it into any React containers.

The Blog

The blog is just a database in Notion where each page has these properties:

  • Name
  • Slug (For links)
  • Status (Draft/Published)
  • Post Date
  • Tags
  • Author (not really necessary)

Each item in the database is a page with the properties you see in the column headers. To get content into this thing I just copied and pasted the markdown from my old blog entries into each page and entered the rest of the data manually (i don’t have many blog posts).

The Connections Popup in NotionThe Connections Popup in Notion

The Resume

I also wanted to keep my resume in notion but I wanted it to use structured data. This would allow me to render it on the website or generate any other format resume using the Notion API . In the end, I ended up with a single page that contained a few different databases that were inter-related. I was able to generate my resume page using a combination of the rendering engine I had built for the blog and combined it with the data model I had put together for the resume.


The External CDN: Cloudflare

One of the surprises I ran into is that if you use file/media columns in Notion, anything you upload from your computer will be stored in Notion’s own protected s3 buckets and the links will expire after a period of time. This works fine if you’re not statically rendering but when you statically generating content, the links to the images will be frozen to the time at which the site was generated.

To workaround that problem, I created a free Cloudflare account and used their R2 buckets to host static imagery and associated to the bucket. I then allowed localhost and domains access to the bucket and uploaded the images I wanted on the site. Then I just added external links to the Notion database.

Checkout Cloudflare’s R2 here. The setup was super-easy and their free tier is pretty generous (for my needs).