Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

Workaround for Notion’s lack of heading levels

The issue with headings

I use Notion as my headless CMS for these blog posts. I recently ran into an issue as I need to add fourth heading level (<h4>) to my blog post but Notion doesn’t render a heading if you try to make a level 4 heading. So I had to come up with a workaround.

As I googled this issue, I ran into this Reddit comment:

For the sake of having further indented headers, I just use H3’s, however for H4 and beyond I put an “→” before the H3 title.

H4 i put: “→ [header text]”

H5 i put: “→ → [header text]”

H6 i put “→ → → [header text]”

This way I can have those Table of Contents linked at the top of the page with slight indentation. If you don’t like that arrow I use then use an emoji or other character-shape... it’s mostly about the concept 🤷🏻‍♂️

While this doesn’t make it semantically proper in Notion, I realized it’s not an issue in my case because the Notion notes are only visible to me and what gets rendered into the blog is a case of its own.

The workaround with notion-render

I use notion-render library to render HTML from Notion’s blocks and what is great about that library is that you can define your own block renderers. It’s a life saver really, I would have already moved away from Notion if it wasn’t for this library.

Here’s my custom renderer for Notion’s Heading 3 elements:

const headingRenderer = createBlockRenderer(
  async (data, renderer) => {
    const content = data.heading_3.rich_text[0].text.content;
    if (content.startsWith("→ ")) {
      /** Since Notion doesn't support heading levels beyond h3,
       * I use this trick to append -> in front of a H3 that I want to be a <h4>
      heading = content.replace("→ ", "");
      return `<h4>${heading}</h4>`;
    } else {
      return `<h3>${content}</h3>`;

What it does is it checks if the heading’s content starts with → and if it does, it renders it into a h4 element instead (and removes the arrow). And if I ever need <h5> or <h6>, I can add extra cases for that in my renderer.

I’m tempted to build my own CMS

Ever since I started writing code as a teenager, there’s been this myth or legend about every (web) developer wanting to build their own CMS and most of the devs failing at it. So I’ve always tried to steer away from that.

But I ran out of features in Ghost and now it’s been about 4 months with Notion and I already have hacks in place with alt texts for images and now heading levels. And I hate long-term hacks. They are great workarounds for short term but shouldn’t be required if using proper tooling.

So I’m considering building a custom headless CMS with Django that fits my specific purpose and allows all sorts of cool things.


Comment by replying to this post in Mastodon.

Loading comments...

Continue discussion in Mastodon »

Syntax Error

Sign up for Syntax Error, a monthly newsletter that helps developers turn a stressful debugging situation into a joyful exploration.