In this guide, we gonna implement a blog using Notion as a content management system.
The @kit/notion
package contains a type-safe client built using zod to validate the data. Our client is built on top of the notionhq/client
package which is the official Notion client.
Create a notion database
The easiest way is to import our database template.
Download the database template.
Blog Template
We recommend you to use our template to make it work with the already existing ui. But you can customize it to your needs.
Import it in your Notion workspace.
Importing the blog database template in Notion
Set your Notion Connection to expose your data
Add a new integration.
In the browser, go to https://www.notion.so/profile/integrations and add a "New integration".
You can let the type
to Public
is not required, you can let it Internal
.
Configure your integration settings.
In "Configuration" tab > Capabilities we gonna need :
- Select "Read content" only (remove the other capabilities)
- Select "No user information" (in the "User capabilities" section)
Set the NOTION_INTEGRATION_SECRET
environment variable.
Show the "Internal Integration Secret" field and copy the value.
Use the copied value to set the NOTION_INTEGRATION_SECRET
environment variable.
NOTION_INTEGRATION_SECRET=your-integration-secret
Set your config file
Create a config/cms.config.ts
file in your application and set the NOTION_INTEGRATION_SECRET
environment variable.
'server-only';
import { parseNotionConfig } from '@kit/notion/config';
import { envs } from '~/envs';
export const notionConfig = parseNotionConfig({
auth: envs().NOTION_INTEGRATION_SECRET,
contentTypes: { // edit this object to fit your needs
// your content types here ...
},
});
Prop | Type | Default |
---|---|---|
contentTypes* | Record<string, { id: string; def: AnyContentTypeDef; }> | |
auth* | string |
Add a new content type
Connect you database
Connect you database to your previously created integration
Connecting your database to your previously created integration
Get your database source id
Getting your database source id
Set the NOTION_POSTS_DB_ID
environment variable.
NOTION_POSTS_DB_ID=your-database-source-id
Set your notion definition in the config file
To let the @kit/notion
package to know the nature of your database, you need to add it in the contentTypes
object.
'server-only'; import { parseNotionConfig } from '@kit/notion/config'; import { envs } from '~/envs'; export const notionConfig = parseNotionConfig({ auth: envs().NOTION_INTEGRATION_SECRET, contentTypes: { posts: { id: envs().NOTION_POSTS_DB_ID, def: { categories: 'multi_select', language: 'select', description: 'rich_text', slug: 'rich_text', image: 'files', status: 'status', title: 'title', }, }, }, });
Use the following tool to get your notion definition.
Once your defintion is fetched, you can refresh your "Internal Integration Secret" if you want to have an easy mind.
Content type definition requirements
Some properties are using internally in the notion client from the @kit/notion
package. For that reason, some properties are required and some optional allow you to implement some specific features.
Required fields
Here is the list of the required fields for a content type definition:
title
:title
slug
:rich_text
(your slug do not must contains spaces)
Optional field
status
:status
(allow you to have adraft
/published
feature -> the pages will be loaded only if the status ispublished
)
If you are implement a roadmap, definition requiements may be different. Check the roadmap definition requirements section.
Translations
You can easily manage your translations with the @kit/notion
package.
Use the language
property in your notion database (alredy present our the database template)
Filter your notion content items with the active i18n language
'use server'; async function BlogPage(props: BlogPageProps): Promise<React.JSX.Element> { const { t, resolvedLanguage: language } = await getServerI18n(); const blogPosts = await notionClient.getContentItems({ contentType: 'posts', filter: { language, }, sort: { by: ['createdTime'], direction: 'desc', }, pagination: { limit, offset, }, content: false, }); return ( <> {/* your page content here */} </> ); }
Environment variables
NOTION_INTEGRATION_SECRET=your-integration-secret
# your database source id ...
NOTION_POSTS_DB_ID=your-database-source-id
Implement your own documentation website with Fumadocs.
Learn how to expose content for AI models.
How is this guide?
Last updated on 10/17/2025