← Back

Build your blog using Notion as a CMS




Tobias Lins


We wanted to launch a blog for a long time, but we missed the rich content editing from Notion in other Content Management Systems. One day I found the following repository from ZEIT. They built a Blog using Notion & NextJS. After much research, I stumbled on chorale.app, a Notion Page renderer. It takes Notion pages and renders them. These projects were example implementations but they don't contain a wrapped up package that could be easily used. Thats why we created react-notion.

react-notion - Render Notion Pages with React

react-notion is born
react-notion is born

I loved the idea and immediately started building a own renderer called react-notion. We took the concepts of the projects mentioned above and built a minimal package that can render nearly all block types of Notion. They also look exactly like the original render!

Our React component requires only one property call blockMap to render the complete content.

notion-api-worker - An simplified Notion API

To get the blockMap you would need to to query the complex private Notion API and thats why we created a serverless wrapper based on Cloudflare Workers. We are using their caching mechanism to get the best response times all over the world <100ms

Building a simple blog

For this example we are going to use Next.js, react-notion, and notion-api-worker

  • Create a new Notion page with a table database template. You can duplicate this page. Each row represents a single blog post. Every Notion page has a pageId in the URL. We need it to query all posts later on. For this example we get following id out of the link: 1099525da7e5405c961706de56622ccd
  • Setup a new Next.js project using the guide provides by Vercel: https://nextjs.org/docs/getting-started and install react-notion to the project. After you have created your first page in /pages/index.jsx we will start implementing our blog overview page.
  • We now implement getStaticProps to fetch all blog posts from Notion.
    • /pages/index.jsx

      import Link from "next/link";
      const NOTION_BLOG_ID = '1099525da7e5405c961706de56622ccd'
      export const getAllPosts = async () => {
      	return await fetch(
        ).then((res) => res.json());

      We now successfully implemented the blog overview page!

  • Now we need to create another page to render the content of each blog post.
    • /pages/[slug].jsx

      import "react-notion/src/styles.css";
      import "prismjs/themes/prism-tomorrow.css";
      import { NotionRenderer } from "react-notion";
      import { getAllPosts } from './'
      export async function getStaticProps({ params: { slug } }) {
        // Get all posts again
        const posts = await getAllPosts();
        // Find the current blogpost by slug
        const post = table.find((t) => t.slug === slug);
        const blocks = await fetch(`https://notion-api.splitbee.io/v1/page/${post.id}`).then((res) => res.json());
        return {
          props: {
      export default ({ post, blocks }) => (
        <div style={{ maxWidth: 768 }}>
          <NotionRenderer blockMap={blocks} />
  • Last, we need to implement getStaticPaths in [slug].jsx to tell Next.js were to find all blog posts.
    • export async function getStaticPaths() {
        const table = await getBlogTable();
        return {
          paths: table.map((row) => `/${row.slug}`),
          fallback: true,
You now have a blog that is powered by Notion.