2 minute read

I have github page blog. It’s a static website based on Jekyll and hosted on the github. A nice solution, free of charge and fast. The only downside was I had to write a markdown page, git commit, and push. I find it disruptive to my writing.

Notion now has APIs and even JavaScript client. It looks pretty easy to use Notion as headless CMS.

In the root directory of my github blog repository, I created package.json and installed NPM packages.

"scripts": {
    "start": "node index",
  "dependencies": {
    "@notionhq/client": "^1.0.4",
    "dotenv": "^16.0.1",
    "notion-to-md": "^2.3.3"
  • @notionhq/client: notion sdk

  • dotenv: environment variables loader

  • notion-to-md: convert notion pages to markdown

First, create a notion integration to get Notion API Key. You need the database id of the page you want to access. The new integration wouldn’t have access to any page by default. So you have to share the page to your integration, by clicking “Share” link on the top right page.

                                  |--------- Database ID --------|

Once you have the API key and the Database ID. query the database to retrieve all pages in there.

import { Client } from '@notionhq/client'
import dotenv from 'dotenv'

const notion = new Client({
  auth: process.env.NOTION_TOKEN,

const response = await notion.databases.query({
  database_id: process.env.NOTION_DATABASE_ID,

Btw, I used console.dir(response, { depth: null })to understand the response structure.

Now, get the content of the each page and write it as markdown file. In Notion SDK, a page consists of blocks and you have to convert each block to the relevant markdown. I used notion-to-md for the job.

import { NotionToMarkdown } from 'notion-to-md'
import * as fs from 'fs'


const n2m = new NotionToMarkdown({ notionClient: notion})
const kebabCase = str => str
const pages = response
  .filter(x => x.properties.Name.title.length > 0)

pages.map(async x => {
  const mdBlocks = await n2m.pageToMarkdown(x.id)
  const content = n2m.toMarkdownString(mdBlocks)

  const createdDate = x.created_time.split('T')[0]
  const createdTime = x.created_time
  const title = x.properties.Name.title[0].plain_text
  const filename = kebabCase(title)
  const tags = x.properties.Tags.multi_select
    .map(x => `  - ${x.name}`)
  const pageContent =
title: ${title}
date: ${createdTime}
  fs.writeFile(`./_posts/${createdDate}-${filename}.md`, pageContent, (err) => {

It generates markdown files according to Jekyll’s frontmatter format. Lastly, I created a github action that runs once a day to retrieve the Notion posts, generate the markdown files, and push the new files to the repository.

name: Build Notion Posts

  - cron: "0 1 * * *"


    runs-on: ubuntu-latest
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
          node-version: 15.x
      - name: Run a multi-line script
        run: |
          touch .env
          echo 'NOTION_TOKEN=$' >> .env
          echo 'NOTION_DATABASE_ID=$' >> .env
          yarn start
      - uses: stefanzweifel/git-auto-commit-action@v4
          commit_message: build pages from notion

Lastly, all the resources I used for this job.