Replacing MDX with Notion

This site you're looking at underwent a Gatsby + MDX to Next.js + Notion transformation. It's still early but it feels like my ideal setup. I can add/edit a post in Notion and see the changes live without a new build.

If you want to use Notion as a CMS, follow this excellent guide to setup the unofficial Notion API and use react-notion to render blocks.

The rest of this post goes through how I customized this flow to get back some of the MDX benefits. Take a look at the source Notion page and corresponding API JSON to follow along.

Custom rendering

react-notion comes with styling that renders your blocks to look like Notion. If you look at the Block component, it's a big render function that switch/cases on the block type. By forking this library, I'm able to render each block type however I want.

Toggle blocks

We're going to take advantage of the toggle block. This block has its own title field as well as children blocks. We can use the title field to identify our block. Then we can handle the children as we want.

An example toggle!

with an underwhelming reveal

One thing I do with toggle blocks is hide content that's referenced elsewhere. If the title of a toggle is Ignore, that block is not rendered. If you look at the Notion source, there's an ignored toggle right here.

Inline tables

Notion tables are great for structured data and can be inlined inside pages. I custom handle Notion tables named Panes. A link to a page inside a Panes table will open a pane. Try this link. And I can put this Panes table inside my Ignore toggle so the table isn't rendered.

Custom components

One reason developers love MDX is the ability to render custom React components inside your markdown. Here's a hacky way to achieve something similar with Notion.

If a toggle block's title looks like <Component>, it's checked against a map of names to React components. A name match will render that component. If the first child block is a JSON code block, that JSON will be parsed and passed as props to the component. Any additional child block is passed as React children, recursively rendered by react-notion. With this setup, you get most of the benefits of MDX custom components.

An example <MyCustomComponent>
{
  "propA": 1,
  "propB": "yummy"
}

This is the first child block.

This is the second child block.

<MyCustomComponent>
props: {"propA":1,"propB":"yummy"}

This is the first child block.

This is the second child block.

If you want to see these pieces in action, check out the Monads for JavaScript developers post. I embed the VSCode editor inside panes that are loaded on demand.

December 17, 2020
Browse more posts or follow on Twitter.