🧑🏼‍🏭 Don't mind us, we are still working on this site using NotionCMS! Like this project? Sponsor us on GitHub: ⚡️⚡️⚡️ Get Started ⚡️⚡️⚡️
⏳: ~7 min read.
🗺️: current location: /notion-cms/guide

The Notion CMS

Give Notion Super Powers.

const myCoolCMS = new NotionCMS({
  databaseId: 'e4bce5b3-1d3a-4afd-b961-10d56cg436aj',
  notionAPIKey: process.env.NOTION as string,

await myCoolCMS.pull()

const postA = myCoolCMS.data['/posts']['/how-to-build-a-blog-with-notion']
const postB = myCoolCMS.data['/posts']['/how-to-use-notion-cms']
const postC = myCoolCMS.data['/posts']['/how-to-make-a-million-with-notion-cms']

// html
// markdown
// plaintext

NotionCMS is a powerful server-land package that lets you leverage Notion as a full-fledged headless CMS. It does this by building a cacheable, serialized data tree from your Notion database. No more jumping through hoops to get your content out of Notion, just the rush of raw information at your fingertips.


  "metadata": {
    "databaseId": "e4fcd5b3-1d6a-4afd-b951-10d56ce436ad",
  "stages": [
  "routes": [
  "tags": [
  "tagGroups": {
    "Lightweight": [
    "Fisher": [
  "siteData": {
    "/treasures": {
      "_key": "/treasures",
            "name": "treasures",
            "slug": "treasures",
            "authors": [],
        "tags": [],
        "path": "/treasures",
            "url": "",
            "content": {
                html: "",
                markdown: "",
                plaintext: ""
      "/a-cache-of-precious-gems": {
          "name": "A cache of precious gems",
          "slug": "a-cache-of-precious-gems",
          "authors": [],
          "tags": [],
          "path": "/treasures/a-cache-of-precious-gems",
          "url": "",
          "content": {
                    html: "",
                  markdown: "",
                plaintext: "" // plugins add other flavors!

Wanna see more? Open up your browser’s console and inspect the page object. This shows the current page’s subset of the NotionCMS tree.


Note: ncms doesn’t run on the client, so what you see in your console has been serialized on the server and sent client side with the rest of the client bundle.

Core Features

🏗️ Framework agnostic - it’s just JS.

🌲 Build a collection-based CMS tree from your Notion database.

🎚️ Leverage database structure to control your routing structure.

⚙️ Geared for Static Site Generation.

📑 Transform Notion blocks → Markdown, plaintext, and (customizable) html.

🗃️ Optimized Content Caching for super fast builds.

🧩 Plugin capable with some powerful core plugins on the way ready to go.

🤯 Provide your own custom renderers for Notion blocks.

🦾 Tagging, filtering, path queries, and tree-walking utilities.

Batteries included!

🔋 Syntax highlighting (highlightjs) out of the box!

Take it for a spin

npm install -D @agency-kit/notion-cms

pnpm add -D @agency-kit/notion-cms

yarn add -dev @agency-kit/notion-cms


Notion is great for managing content and has an excellent API and SDK. However, leveraging Notion as a headless CMS in production can be challenging.

Until recently, Notion did not support sub-pages (sub-items in a database). As a result, most existing Notion-as-a-headless-CMS solutions cannot leverage this new feature, which is crucial for building a collection-based headless CMS.

Another obstacle is that pulling content from Notion can be time-consuming. Responses can take a few seconds, the API provides a lot of data to sift through, and multiple calls to different endpoints are required to go from the CMS database to the content for each page. All of this together results in a suboptimal developer experience when using a static site generator that often makes requests on each build.

NotionCMS addresses each of these issues and provides an excellent developer experience while using Notion as your headless content management system.

When should I use NotionCMS instead of the Notion API SDK directly?

The Notion API SDK is a great tool and there are certainly times where you would want to use that directly instead of Notion CMS. Here are a few scenarios that may help you choose the best tool for the job. 🛠️

Data structure


Use Notion API SDK when you need total control over the final data structure of content pulled from Notion.


Use NotionCMS when you can leverage a standardized data tree to keep your application logic simple.

Content structure in Notion


Use Notion API SDK when your Notion content that you want to fetch lives in various places in Notion and isn’t organized in unified database structure.


Use NotionCMS when you have a single database that you want to use to define your website’s structure. Entries in your database might correspond to pages or collections of items that each make sense as a route in your website or application.

Starting from scratch and not sure how you want to structure your data? NotionCMS provides a great starting point for projects that is simple to scale as your project grows.

Rendering Pipeline


Use Notion API SDK when you want total control over the process of translating Notion API result blocks into your client’s content format of choice


Use NotionCMS when you want to benefit from a pre-existing pipeline for translating Notion API results to html or other formats using NotionCMS plugin ecosystem.

Design philosophy

NotionCMS is really geared towards a server-side environment, so its intended usage is in Node js.

This is because pulling content from Notion (but really more generally) and rendering it on a client is both a poor experience for the end user and terrible for SEO. Even if you are building a SPA, you probably want to prerender it.

That’s what NotionCMS is built for, though there is an issue for opening the door for client side usage, so if you have a use case feel free to chime in there.

Getting Started

In order to use NotionCMS, you have to subscribe to a specific database structure with a few core properties.

Its an extremely generic design that gives you all the things you need for basic sites but lends flexibility for types of content other than the standard web page, blog post etc.

NotionCMS leverages nested database items so you can build collections.

NotionCMS leverages nested database items so you can build collections.

See the structure in this template https://cooked-shovel-3c3.notion.site/Community-e9fce377adb1425da8ac3ef3acef2bc2

The first step to using NotionCMS is to make a NotionAPI integration and grant the integration access to the Notion page where your database will be stored. You will need to make a copy of the template database (recommended) and then copy both the database ID and your Notion API key into your project (using something like env variables).

Here’s a tutorial for setting up the Notion integration since it’s an essential step.

Example Usage

// initialize
const myCoolCMS = new NotionCMS({
  databaseId: 'e4fcd5b3-1d6a-4afd-b951-10d56ce436ad',
  notionAPIKey: process.env.NOTION,
  // Other options

// Pull down all Notion content
// fetch returns a promise with the siteData structure
await myCoolCMS.fetch()

// Access the routes here:

// Access the page content here:

// Access paths like this:
const postA = myCoolCMS.data['/posts']['/how-to-build-a-blog-with-notion']
const postB = myCoolCMS.data['/posts']['/how-to-use-notion-cms']


Tip: If you’re using NotionCMS for the first time, make sure that at least some of your pages have the Published select set to Published, or use the draftMode option or you won’t see any content after running pull!

Helper Functions

// returns an array of only child pages of a page looked up using the key.
myCMS.filterSubPages('/path-segment' /* or Page reference*/)

// returns page reference

// Get tagged collections this way or by passing a single tag:
const tagged = myCoolCMS.getTaggedCollection(['blog', 'programming'])

// Walk through all nodes in the CMS tree and perform some action
myCoolCMS.walk(node => console.log(node.name, node.path))

//async walk
await myCoolCMS.asyncWalk(async node => await /* some async action*/)

// Export the CMS object
// uses cache location by default and exports an ugly but optimized cache

// Export prettified CMS object, wherever you want.
myCoolCMS.export({pretty: true, path: './debug/my-cool-cms.json'})

// Import an exported CMS object

Advanced Usage

const myAdvancedCMS = new NotionCMS({
  databaseId: 'e4fcd5b3-1d6a-4afd-b951-10d56ce436ad',
  notionAPIKey: process.env.NOTION,
  rootUrl: 'https://mycoolsite.com',
  localCacheDirectory: './localcache/',
  refreshTimeout: 'one hour', // Or a number, 60 * 1000 * 60
  plugins: [customPlugin()],

await myAdvancedCMS.pull() // alias for fetch, both work the same.

See the full API reference here.

Extending with Plugins

NotionCMS is designed to be highly extensible and supports a variety of plugins to help you streamline your workflows when building websites with content from Notion.. Whether you need to add custom logic to your templates, declare Vue components right in your Notion page, or integrate with third-party services, NotionCMS makes it easy to extend the functionality of your headless CMS. Check out the ➡️ Plugins page ⬅️ for more information on using existing plugins or the 🏗️ building your own plugins guide for creating custom workflows with NotionCMS.

we make notionware. 2023 \c\