How to Create Next.js Blog Using Notion as a CMS


How to Create Next.js Blog Using Notion as a CMS


- 6 min to read

How to Create Next.js Blog Using Notion as a CMS

Developer blogs can get complicated, and that’s great if you want to test a new tool or setup. But if your goal is a continuous update of a blog with new content, you need to keep the publishing process as simple as possible. Otherwise, you will never post as much as you want. One way you can do this is by setting up your blog using Notion as a CMS via the Notion public API.

By Mary Gathoni

How to Create Next.js Blog Using Notion as a CMS

Notion is an all-in-one workspace for taking notes, creating docs, wikis, and managing projects. A blog is a natural extension. Publishing a blog post will be as simple as creating a new row in a Notion database. Here is the guide on how to do it using Next.js.


To complete this tutorial, you will need

Setting Up Notion

Before using Notion as a CMS for your blog, you must create a new integration and share it with the Notion database with the blog posts.

Creating a New Notion Integration

A Notion integration allows you to connect to a Notion account via the Notion API. Once you create it, you should get a Notion token that’ll give you access to the blog data.

Begin by signing up for a free Notion account if you don’t have one yet.

Then, follow the instructions in the getting started guide to create a new Notion integration.

Lastly, copy the Notion token to the .env file.

Creating a Notion Database

Follow these steps to create a Notion database that will hold the blog posts. You must also share the integration with the database to ensure you can access it using the token.

Additionally, you need the ID of the Notion database. It acts as an identifier for the database you want to connect to.

Retrieve it from the database URL by copying the part corresponding to the database_id in the example below.{workspace_name}/{database_id}?v={view_id}

Populate the Notion Database

After creating the database, you need to populate it. First things first, create the blog schema by adding the following fields:

  • Name - the title of the blog post
  • Slug - the URL of the post
  • Description - a list of users that wrote the post
  • Tags - the URL of the meta image for a post
  • Published - indicates whether the post is published or not; only the published posts will be displayed on the blog
  • Date- the date the post was published

Each row in the database is a blog post page containing the slug, description, tags, date, and published status.

You can add extra fields, like an author field, and customize the blog schema according to your preference.

Setting Up Next.js

Next.js is a React framework for production. It comes with built-in features and optimizations for creating fast applications without extra configuration. It is perfect for a static blog as you can pre-render pages and configure the app to routinely update the site through the incremental static regeneration (ISR) feature.

Start setting up a new Next.js project by running the create-next-app command on the terminal.

npx create-next-app@latest

The command will prompt you for a project name. Type your preferred name and hit enter.

After the installation completes, open the folder using a text editor and clean up /pages/index.js to look like this:

You can now start creating the blog overview page but first fetch the blog posts from Notion.

Fetch All Published Blog Posts From Notion

You’ll create a function to fetch all the published posts metadata using the notionhq/client package, a JavaScript client that simplifies making requests to the Notion API.

So, run the following command in the terminal to install it.

npm install @notionhq/client

All the functions that query Notion will go in the /lib/notion.js file. Create it and add the following code to initialize the Notion client.

Remember to add the NOTION_TOKEN and the DATABASE_ID to the .env file.

Next, create a new function called getAllPublished. It uses the notion client to query the database for published posts only using the filter option. It also sorts the posts in descending order based on the date they were created.

The getPageMetaData function extracts only the necessary data from the returned results.

You are also using the getToday function to format the date from Notion to a more human-readable format.

Now, you are ready to create the blog overview page.

Creating the Blog Overview Page

For better performance, you will fetch the blog posts at build time using getStaticProps and pre-render the page.

Modify /pages/index.js, to use getStaticProps where you will fetch the posts by calling the getAllPublishedPosts function.

The getStaticProps function returns the posts data and makes it available to the Home component through props.

Once the Home component receives the props, it uses the map function to iterate over the array of posts and renders them on the page.

Using revalidate with a value of 60 in getStaticProps tells Next.js to attempt to regenerate the page every minute, which in turn ensures the blog stays up to date.

Fetch a Single Blog Post From Notion

Before creating the individual blog pages, you must fetch the content of each post from Notion.

For this, you will use the notionhq/client package you installed earlier and the notion-to-md package to convert Notion blocks to markdown.

Install notion-to-md by running this command.

npm install notion-to-md

To use notion-to-md, first give it the Notion client as an option.

For a given slug, this function retrieves a post from Notion and converts the page to markdown. It returns the markdown and page metadata in an object.

Displaying a Single Post

It's not feasible to manually create a page for each post. It's easier to create a dynamic route that renders the pages based on their corresponding id, slug, or other parameters. In Next.js, you create the dynamic route by adding a bracket around the page name.

Create a new dynamic page called /pages/posts/[slug].js and add the following code.

getStaticProps uses the slug parameter from the path URL to identify the blog post that should be rendered. Remember you named the page [slug].js, and that’s why you are using params.slug.

Note the revalidate option is set to 60 in the return statement. Like in the blog overview page, this tells Next.js to try fetching the data and updating the page after a minute.

getStaticProps is not enough to pre-render the list of posts. You need to define the paths using getStaticPaths, so add the following to the /pages/posts/[slug].js.

Here, the paths are generated from the slug of each post returned from Notion.

After fetching the post, the next step is to display it. Having converted the Notion blocks to markdown using notion-to-md, you now need to convert the markdown to HTM to render it on the browser. For this, you can use the React Markdown package. Install it by running this command.

npm install react-markdown

Next, import react-markdown and modify the Post component in /pages/posts/[slug].js like this:

That’s it! You have successfully created a static blog using Notion as a CMS.

To make it more visually appealing, add syntax highlighting to code blocks.

Highlight Code Blocks

There are many tools you can use to highlight code blocks. One of them is the react-syntax-highlighter component. It is easy to use and comes with out-of-the-box styling.

Run this command in the terminal to install the react-syntax-highlighter component.

npm install react-syntax-highlighter

Then, modify the Post component in /pages/posts/[slug].js.

Note you are using the vscDarkPlus theme to highlight the code. You can, however, choose another theme if you like.

Styling the Blog

Copy the following styles in Home.module.css to style the blog.

These styles are just enough to give the blog structure, so feel free to change them as you see fit.

Deploy the Blog to Vercel

One of the easiest ways to deploy the blog is to use Vercel. Start by pushing your code to GitHub, then follow the Vercel for Git instructions from this guide, and your site will be available online. Remember to add the Notion token and database ID as environmental variables while deploying your site.

Deploy the Blog to Netlify

Apart from Vercel, you can also deploy the blog for free to the Netlify platform from Git. Push your code to GitHub or a supported version-control tool of your choice, then follow these instructions to deploy to Netlify. Like deploying to Vercel, you should include the Notion token and database ID as environment variables.

Pros and Cons of Using Notion as a CMS

A Notion-powered blog is easy to maintain. You only need to edit or create rows in the database to update the blog’s content. If you are already a Notion user, publishing content fits easily into your daily workflow.

Notion is free for individual use. And if you end up hosting your blog on a platform like Vercel or Netlify, the site will be 100% cost-free.

However, there are some challenges to rendering pages from Notion in Next.js.

Notion returns short-lived image URLs that expire after an hour. For statically rendered pages, this means users experience a bunch of 404 errors. A workaround would be to host your images yourself instead of relying on Notion.

Note that if you want to render markdown tables from the Notion page in Next.js using react-markdown, you must use the remark-gfm plugin. Otherwise, any table in the blog post will not render correctly. It is because react-markdown follows the CommonMark standard that doesn’t have tables.


In this tutorial, you created a Next.js blog powered by Notion. To update or create new posts, edit the Notion database, and the blog will update every minute. For the entire code, go to the GitHub repo or see the deployed blog. You can also duplicate this sample Notion database as a starting point.


Written by

Mary Gathoni Writing Program Member

I am a front-end developer passionate about the web and serverless technologies. I believe in continuous learning and I create technical content to not only understand concepts better but also help others learn them.

Readers also enjoyed