This project combines the power of Notion as a CMS with modern web development tools like Next.js, providing a flexible foundation for your content management needs.
This guide will walk you through the setup process and essential features of this boilerplate.
I love Notion and imho it’s a great (and free) tool that can work as a CMS.
There are some cool projects (e.g. react-notion-x) that can be used for the same purpose, but I wanted to base my solution on an official Notion tools.
I had tons of fun playing with their API to render components and show everything I needed to create my personal website, so I’ve decided to make this project open-source.
I hope it can be helpful to someone.
You can check the demo here.
To implement this project, you would need to duplicate Notion database to your workspace, create a new Notion integration to get an API key, clone the notion-as-a-cms repository, configure the environment variables with your Notion credentials, and deploy the Next.js application to your preferred hosting platform.
First, you need to duplicate the project’s Notion database - available here.
It contains the list of pages that would be rendered in your project. After cloning it, you should see Getting started page. Don’t remove it yet, it would be helpful to ensure that everything in the setup has been done correctly.
Once you’ve duplicated the database, save it’s ID - it would be necessary in project setup. You can check it in the Notion url -https://www.notion.so/<DB_NAME>-<DB_ID>
Each item in the database is a page that contains properties and content.
The properties are:
- Slug
- Meta title
- Meta description
- SEO Keywords
A slug would be necessary to render your page. Home page uses home by default.
After creating a new page, simply navigate to <yourdomain>.<extension>/<slug> or localhost:300/<slug> for local development.
To fetch the data, it’s necessary to create a Notion integration. It can be an internal integration. Follow the guide to create one. Save your Internal Integration Secret, it would be necessary in project setup.
After creating an integration, give the permissions (as the guide explains) to the duplicated database in your Notion space.
To start, simply use this template to create a new GitHub repository and clone it.
Then, copy the .env.example and rename it to .env. Use your Internal Integration Secret and Database ID (from previous steps) as NOTION_SECRET and NOTION_DATABASE_ID.
Optionally, set SITE_THEME to pick a style theme (notion — the default — or minimal). It falls back to notion when unset, so you can deploy the same codebase to multiple sites with different looks just by changing this one variable.
It’s all.
You can run npm run dev.
If everything went properly, you should see this Getting started page.
Now, feel free to remove it, edit the content and do whatever you want.
The whole project is based on a Next.js app. The easiest way to deploy it is to use the Vercel Platform from the creators of Next.js.
Check out the Next.js deployment documentation for more details.
In this project, images are displayed with a specific width and height to maintain a consistent aspect ratio. This is necessary because Next.js requires both width and height to properly optimize and render images.
The default width and height are defined in the constants.ts file:
export const defaultWidth = 500;
export const ratio = 16 / 9;
export const defaultHeight = defaultWidth / ratio;You can adjust the defaultWidth and ratio values to change the dimensions of the images. The defaultHeight is automatically calculated based on the width and ratio.
For example, if you want to change the aspect ratio to 4:3, you can update the ratio value:
- export const ratio = 16 / 9;
+ export const ratio = 4 / 3;This will ensure that all images maintain the new aspect ratio while being rendered.
Themes restyle the same Notion content — they swap the palette, fonts, and a few element rules; they don't change the page structure. Each theme lives in its own folder under src/themes/<name>/ and is activated per deployment with the SITE_THEME env var (unknown/empty falls back to notion). The repo ships with notion (the default) and minimal.
This repo bundles an add-theme skill for Claude Code. Just point it at a design and ask — it scaffolds the theme, wires everything up, and verifies it builds and renders:
Add a theme based on this design: ./mockup.png
It also accepts a text brief (“warm cream background, serif headline, terracotta accent”), a code/markup prototype, or a URL/Figma link. The skill triggers on phrasing like “add/create a theme”, “make the site look like this”, or any mockup/screenshot you hand it. It follows the manual steps below for you and screenshots the result against your design.
Using src/themes/minimal/ as a template, touch these five places (replace <name> with your theme, lowercase, no spaces):
-
src/themes/<name>/fonts.ts— load the theme’s fonts vianext/fontand export their CSS variables (usepreload: falseso other deployments don’t ship them):import { Fraunces } from "next/font/google"; const fraunces = Fraunces({ subsets: ["latin"], weight: ["400"], variable: "--font-fraunces", preload: false }); export const fontVariables = [fraunces.variable];
-
src/themes/<name>/<name>.css— the theme block. Scope everything to[data-site-theme="<name>"], override the tokens, point the semantic font vars at your fonts, and restyle the elements you care about:[data-site-theme="<name>"] { --notion-background: #0e0e10; --notion-default: #e7e2d6; --font-body: var(--font-inter), system-ui, sans-serif; --font-display: var(--font-fraunces), Georgia, serif; } [data-site-theme="<name>"] h1 { font-family: var(--font-display); color: #f5efe1; } [data-site-theme="<name>"] a { border-bottom: 1px solid #b4533a; text-decoration: none; }
Pick the accent/link color straight from the design and write it into the theme CSS. Only override what the design changes — everything else inherits the
notionbase. -
src/themes/index.ts— register the name:- const themeNames = ["notion", "minimal"] as const; + const themeNames = ["notion", "minimal", "<name>"] as const;
-
src/app/globals.css— import the theme CSS afternotion(cascade order = priority):@import "../themes/notion/notion.css"; @import "../themes/minimal/minimal.css"; + @import "../themes/<name>/<name>.css"; -
src/themes/fonts.ts— add the theme’s fonts to the aggregator that’s applied to<html>:+ import { fontVariables as nameFonts } from "./<name>/fonts"; - export const fontVariables = [...notionFonts, ...minimalFonts].join(" "); + export const fontVariables = [...notionFonts, ...minimalFonts, ...nameFonts].join(" ");
Then set SITE_THEME=<name> and run npm run dev. Restart the dev server after editing globals.css or theme CSS — the Turbopack dev server doesn’t reliably hot-reload them (rm -rf .next if it stays stale). Note: a design that needs a different page structure (header/footer, multi-column) requires a per-theme layout component, which isn’t part of this CSS-only theming.