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
- Node.js, npm, and git installed
- a text editor such as Visual Studio Code or Atom
- a web browser such as Firefox or Chrome
- familiarity with Next.js. You can look at the official Next.js getting started tutorial to learn more.
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
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.
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.
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
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.
/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
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
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.