Build markdown-based static sites with Notion.
- Use Notion to write and organize pages
notion-markdown-cms sync
to build a markdown repository- run your favourite static site generator (VuePress, Docusaurus, Gatsby, ...)
Success! 🚀
- uses the official Notion API only
- written in typescript/javascript
- renders page properties to frontmatter
- recursively traverses the Notion Block graph to include database pages, child pages
- renders an index file of all your pages so you can easily build Navs/Sidebars
The following Notion API block object types are supported:
Block Type | Supported | Notes |
---|---|---|
Paragraph | ✅ Yes | |
Heading1-3 | ✅ Yes | |
Callout | ✅ Yes | |
Quote | ✅ Yes | |
Bulleted List | ✅ Yes | |
Numbered List | ✅ Yes | |
To do | ✅ Yes | |
Toggle | ✅ (Yes) | Toggle content is included, however the toggle header is not |
Code | ✅ Yes | An html block starting with <!--notion-markdown-cms:raw--> is rendered as raw HTML and not as a fenced block |
Child Pages | ❌ not planned | avoid, they don't mix well with clear site navigation |
Child Databases | ✅ Yes | renders as table + including child pages, inline-only tables planned |
Embed | ❌ Missing | unclear, might be undesireable for static sites |
Image | ✅ (Yes) | captions not supported yet |
Video | ❌ Missing | |
File | ❌ Missing | |
❌ Missing | ||
Bookmark | ✅ Yes | use a caption as a link name |
Equation | ❌ Missing | |
Divider | ✅ Yes | |
Table Of Contents | ❌ not planned | static site generators have their own ToC implementations |
Breadcrumb | ❌ not planned | static site generators have their own nav implementations |
Synced Block | ✅ Yes | renders all children blocks |
Support for other block types can be considered once they are available on the official Notion API.
The following Notion API rich text types are supported
Rich Text Type | Supported | Notes |
---|---|---|
Text | ✅ Yes | |
Mention | ✅ partially | Page mentions only, mentioned pages are included |
Equation | ❌ Missing |
The following annotations (and any combination thereof) are supported:
Annotation | Supported | Notes |
---|---|---|
bold | ✅ Yes | |
italic | ✅ Yes | |
strikethrough | ✅ Yes | |
underline | ✅ Yes | |
code | ✅ Yes | |
color | ❌ not planned | not available in markdown |
The following Notion API page property types are supported
Propety type | Supported | Notes |
---|---|---|
Rich text | ✅ Yes | rendered as plaintext string |
Number | ✅ Yes | |
Select | ✅ Yes | rendered as name |
Multi Select | ✅ Yes | rendered as array of names |
Date | ✅ Yes | rendered as string |
Formula | ❌ missing | |
Relation | ✅ Yes | rendered as array of page ids |
Rollup | ❌ missing | |
Title | ✅ Yes | used as page title |
People | ✅ Yes | rendered as comma-separated list of names |
Files | ❌ missing | |
Checkbox | ❌ missing | |
Url | ✅ Yes | rendered as string |
✅ Yes | rendered as string | |
Phone Number | ✅ Yes | rendered as string |
Created time | ✅ Yes | rendered as string |
Created by | ✅ Yes | rendered as name |
Last edited time | ✅ Yes | rendered as string |
Last edited by | ✅ Yes | rendered as name |
At the moment notion-markdown-cms
is meant to be consumed via its node.js API from build scripts
wrapping your favourite static site generator tool. You can install it from npm
npm add "@meshcloud/notion-markdown-cms"
You can find an example build script using the node.js API below. Consult the SyncConfig reference for documentation of available configuration options.
A CLI tool could be made available later.
import { slugify, SyncConfig, sync } from "notion-markdown-cms";
const config: SyncConfig = {
cmsDatabaseId: "8f1de8c578fb4590ad6fbb0dbe283338",
pages: {
destinationDirBuilder: (page) => slugify(page.properties.get("Category")),
frontmatterBuilder: (page) => ({
id: page.meta.id,
url: page.meta.url,
title: page.meta.title,
category: page.properties.get("Category")
}),
},
databases: {
"fe9836a9-6557-4f17-8adb-a93d2584f35f": {
sorts: [
{
property: "Scope",
direction: "ascending",
},
{
property: "Cluster",
direction: "ascending",
},
],
renderAs: "pages+views",
pages: {
destinationDirBuilder: (page) => slugify(page.properties.get("Scope")),
frontmatterBuilder: (page) => ({
id: page.meta.id,
url: page.meta.url,
title: page.meta.title,
cluster: page.properties.get("Cluster")
}),
},
views: [
{
title: "By Scope",
properties: {
groupBy: "Scope",
include: ["Name", "Scope", "Cluster", "Summary"],
},
},
},
},
};
async function main() {
const notionApiToken = process.env.NOTION_API_TOKEN;
if (!notionApiToken) {
throw new Error(
"Required NOTION_API_TOKEN environment variable not provided."
);
}
rimraf.sync("docs/!(README.md)**/*");
// change into the docs dir, this simplifies handling relative paths
process.chdir("docs/");
const rendered = await sync(notionApiToken, config);
// do something with the rendered index, e.g. writing it to a file or building a nav structure
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
There are quite a few alternatives out there already, so why did we build notion-markdown-cms
?
Below table, albeit subjective, tries to answer this.
Project | Notion API | Language | Rendering Engine | Output looks like |
---|---|---|---|---|
Nortion Markdown CMS | ✅ official | TypeScript | Markdown + JS Index | Site generator theme |
Notion2GitHub | Python | Markdown | Site generator theme | |
notion-cms | TypeScript | React | Notion App | |
vue-notion | JavaScript | Vue.js | Notion App | |
react-notion | JavaScript | React | Notion App |
For convenient development you can use
nix-shell
to set up a development environemnt- You'll need a Notion database for testing. You can e.g. copy one of these to your own Notion Workspace
- A Notion API Token
As this project is still in its very early stages,
notion-markdown-cms
does not come with its own demo, example or test cases yet.