Building a Notion Blog

By: JJ Kasper
Posted: January 03, 2020

While updating our blog on we wanted to come up with a way to get a better editing experience than we previously had using MDX. Our solution to getting this great editing experience was to use Notion of course. Read on to see how we reverse engineered Notion's private API to build a blazing fast blog leveraging Next.js and ZEIT!

Getting Started

One of the first steps was to figure out how we wanted to structure our blog posts in Notion. We first created a parent blog page and had each post as a sub-page. Then we created a table on the parent blog page with a reference to each posts' page and it's metadata. After doing this we realized in Notion we could streamline it even more by making one of the columns on the table the page instead of a sub-page. Now anytime we were to make a new blog post all we would have to do is click "New" on the table to add a new row!

Next came the process of figuring out how to query the data from the tables and maintain a blazing fast blog. This was a very iterative process and we learned a lot during it. One of the things we learned while working on this was that pre-rendering essential data can be very important for an extremely fast experience. After learning this we set out to enhance the static-site generation capabilities of Next.js.

Reverse Engineering Notion's Data

Since Notion hasn't yet launched a public API and we didn't want to let that hold us back we decided to figure out how to pull the data using their internal APIs. There were a few APIs that we noticed we would mainly need which were: loadPageChunk, queryCollection, and getRecordValues. Learning how these APIs worked mostly consisted of loading up our blog page and watching the network requests and responses. As you can see in the recording above we noticed you can find the page's id in the URL and in the loadPageChunk request payload.

Rendering the Data

Now that we figured out how to load the data, we need to figure out how to render it while still having the flexibility that MDX offered but the speed that our static-site generation approach offers. One way we came up with to be able to still leverage custom components from within Notion was to use the code block, we set a rarely used language to identify it as a custom component and then render it using the react-jsx-parser library.

An image from Notion

The above approach gives us a pretty great experience, we are able to use most built-in blocks in Notion to have a good editing experience and then fallback to our custom components approach only when we have to. We even investigated how we could load assets from Notion like videos and images since being able to locate these within the post can also help streamline the process.

Managing the Posts

Above you can see the process of creating a new blog post using Notion. Since we even store the slug in Notion and leverage the dynamic routing feature in Next.js we don't have to change the logic in our application to accommodate more posts. Mainly the only time we need to change our application logic is when we want to expose a new custom component to utilize in Notion.

Since we're using Next.js' upcoming static-site generation support we get a very fast and reliable experience in production. On top of that when editing locally with your application in development you can still view your draft posts and see the changes as you write.

Getting to use Notion for our blog has been a pretty cool update. We no longer have to manage the painful editing of JSX/MDX and attempting to stay in sync as you write for a simple blog posts. We also get the real time versioning on Notion which can be a lifesaver when rushing against a deadline for your post. I'm looking forward to the public Notion API and seeing all of the things the community will be able to create and enjoy leveraging all of these awesome technologies put together!

Thanks for giving this a read and I hope you're as excited about all of the cool things we can build when we bring great technology together as I am. Until next time 👋