diff --git a/README.md b/README.md
index 9fcb537..8dc2b92 100644
--- a/README.md
+++ b/README.md
@@ -11,22 +11,37 @@

## Table of contents
- - [Description](#description)
- - [Installation](#installation)
- - [Examples](#examples)
- - [Basic example](#basic-example)
- - [Blog with Notion as CMS](#blog-with-notion-as-cms)
- - [Notion page to single page](#notion-page-to-single-page)
- - [Usage](#usage)
- - [Override built-in components (new)](#override-built-in-components-new)
- - [Giving Styles](#giving-styles)
- - [...moreProps](#moreprops)
- - [Custom Components](#custom-components)
- - [Display a custom table of contents](#display-a-custom-table-of-contents)
- - [Guides](#guides)
- - [How to use code blocks](https://github.com/9gustin/react-notion-render/wiki/About-code-blocks-and-how-to-colorize-it-%F0%9F%8E%A8)
- - [Supported blocks](#supported-blocks)
- - [Contributions](#contributions)
+- [Table of contents](#table-of-contents)
+- [Description](#description)
+- [Installation](#installation)
+- [Examples](#examples)
+ - [Basic example](#basic-example)
+ - [Fetching data with @notionhq/client](#fetching-data-with-notionhqclient)
+ - [Blog with Notion as CMS](#blog-with-notion-as-cms)
+ - [Notion page to single page](#notion-page-to-single-page)
+- [Usage](#usage)
+ - [Override built-in components (new)](#override-built-in-components-new)
+ - [How works?](#how-works)
+ - [Mapping page url](#mapping-page-url)
+ - [Giving styles](#giving-styles)
+ - [Using default styles](#using-default-styles)
+ - [Using your own styles](#using-your-own-styles)
+ - [...moreProps](#moreprops)
+ - [Custom title url](#custom-title-url)
+ - [Preserve empty blocks](#preserve-empty-blocks)
+ - [Custom components](#custom-components)
+ - [Link](#link)
+ - [Image](#image)
+ - [Video](#video)
+ - [Display a custom table of contents](#display-a-custom-table-of-contents)
+- [Guides](#guides)
+ - [How to use code blocks](#how-to-use-code-blocks)
+- [Supported blocks](#supported-blocks)
+- [Contributions:](#contributions)
+ - [Running the dev example](#running-the-dev-example)
+ - [Running another example](#running-another-example)
+ - [Project structure](#project-structure)
+- [License](#license)
## Description
@@ -62,6 +77,89 @@ export const getStaticProps = async () => {
}
```
+### Fetching data with @notionhq/client
+
+```jsx
+// e.g. /lib/notion-cms.ts
+
+import { Client } from '@notionhq/client'
+
+// Initialize a new client
+const DATABASE_ID = '54d0ff3097694ad08bd21932d598b93d'
+const notion = new Client({ auth: process.env.NOTION_TOKEN })
+
+export const fetchPages = cache(() => {
+ return notion.databases.query({
+ database_id: DATABASE_ID,
+ filter: {
+ property: "status",
+ status: {
+ equals: "live",
+ }
+ } // if you have a status property for controlling the post status, you can easily filter it
+
+ })
+})
+
+// If you have a slug property you can use this function to get the page by slug
+export const fetchBySlug = cache((slug: string) => {
+ return notion.databases.query({
+ database_id: notionBlogDatabaseId,
+ filter: {
+ property: "slug",
+ rich_text: {
+ equals: slug,
+ },
+ }
+ })
+ .then((res) => res.results[0] as PageObjectResponse | undefined)
+})
+
+// OR - you can simply just use the page id
+export const getPageById = cache((pageId: string) => {
+ return notion.pages.retrieve({
+ page_id: pageId,
+ })
+})
+
+// This is a function to get the blocks of a page (and the children blocks)
+export const getBlocksWithChildren = cache(async (pageId: string) => {
+ const response = await notion.blocks.children.list({
+ block_id: pageId,
+ })
+ const blocks = response.results as BlockObjectResponse[];
+
+ const childBlocks = await Promise.all(
+ blocks
+ .filter((block) => block.has_children)
+ .map(async (block) => {
+ return {
+ id: block.id,
+ children: await fetchPageBlock(block.id)
+ }
+ })
+ );
+
+ const blocksWithChildren = blocks.map((block) => {
+ // Add child blocks if the block should contain children but none exists
+ if (block.has_children) {
+ const blockType = block.type;
+ if (blockType in block && !(block as any)[blockType].children) {
+ (block as any)[blockType].children = childBlocks.find(
+ (x) => x.id === block.id
+ )?.children;
+ }
+ }
+ return block
+ });
+
+ return blocksWithChildren;
+})
+```
+
+After initializing this, you can use the functions in the example above to get the blocks of a page. See the basic example above.
+
+
### Blog with Notion as CMS
I've maded a template to blog page, that use this package and allows you have a blog using notion as CMS.
@@ -177,6 +275,8 @@ This is independient to the prop **useStyles**, you can combinate them or use se
| rnr-table_of_contents | Table of contents | ul |
| rnr-table | Table | table |
| rnr-table_row | Table row | tr |
+| rnr-column_list | ColumnList | div |
+| rnr-column | Column | div |
**Text Styles**
@@ -344,6 +444,7 @@ Most common block types are supported. We happily accept pull requests to add su
| Table Of Contents | ✅ |
| Table | ✅ |
| Synced blocks | ✅ |
+| Columns | ✅ |
| Web Bookmark | ❌ |
## Contributions:
diff --git a/dev-example/data/columnListBlocks.json b/dev-example/data/columnListBlocks.json
new file mode 100644
index 0000000..96eedc7
--- /dev/null
+++ b/dev-example/data/columnListBlocks.json
@@ -0,0 +1,168 @@
+{
+ "object": "list",
+ "results": [{
+ "object": "block",
+ "id": "dummy-id-1",
+ "parent": {
+ "type": "page_id",
+ "page_id": "dummy-page-id-1"
+ },
+ "created_time": "2024-12-05T10:34:00.000Z",
+ "last_edited_time": "2024-12-05T10:34:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "has_children": true,
+ "archived": false,
+ "in_trash": false,
+ "type": "column_list",
+ "column_list": {
+ "children": [
+ {
+ "object": "block",
+ "id": "dummy-id-2",
+ "parent": {
+ "type": "block_id",
+ "block_id": "dummy-id-1"
+ },
+ "created_time": "2024-12-05T10:34:00.000Z",
+ "last_edited_time": "2024-12-05T10:34:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "has_children": true,
+ "archived": false,
+ "in_trash": false,
+ "type": "column",
+ "column": {
+ "children": [
+ {
+ "object": "block",
+ "id": "dummy-id-3",
+ "parent": {
+ "type": "block_id",
+ "block_id": "dummy-id-2"
+ },
+ "created_time": "2024-12-05T10:34:00.000Z",
+ "last_edited_time": "2024-12-05T13:23:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "has_children": false,
+ "archived": false,
+ "in_trash": false,
+ "type": "paragraph",
+ "paragraph": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "LeftCol",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "LeftCol",
+ "href": null
+ }
+ ],
+ "color": "default"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "object": "block",
+ "id": "dummy-id-4",
+ "parent": {
+ "type": "block_id",
+ "block_id": "dummy-id-1"
+ },
+ "created_time": "2024-12-05T10:34:00.000Z",
+ "last_edited_time": "2024-12-05T10:34:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "has_children": true,
+ "archived": false,
+ "in_trash": false,
+ "type": "column",
+ "column": {
+ "children": [
+ {
+ "object": "block",
+ "id": "dummy-id-5",
+ "parent": {
+ "type": "block_id",
+ "block_id": "dummy-id-4"
+ },
+ "created_time": "2024-12-05T10:34:00.000Z",
+ "last_edited_time": "2024-12-05T13:23:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "dummy-user-id-1"
+ },
+ "has_children": false,
+ "archived": false,
+ "in_trash": false,
+ "type": "paragraph",
+ "paragraph": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "RightCol",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "RightCol",
+ "href": null
+ }
+ ],
+ "color": "default"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }]}
\ No newline at end of file
diff --git a/dev-example/lib/notion.js b/dev-example/lib/notion.js
index 7219a81..be94cb1 100644
--- a/dev-example/lib/notion.js
+++ b/dev-example/lib/notion.js
@@ -22,3 +22,38 @@ export const getBlocks = async (blockId) => {
})
return response.results
}
+
+
+
+export const getBlocksWithChildren = async (pageId) => {
+ const response = await notion.blocks.children.list({
+ block_id: pageId,
+ })
+ const blocks = response.results;
+
+ const childBlocks = await Promise.all(
+ blocks
+ .filter((block) => block.has_children)
+ .map(async (block) => {
+ return {
+ id: block.id,
+ children: await getBlocksWithChildren(block.id)
+ }
+ })
+ );
+
+ const blocksWithChildren = blocks.map((block) => {
+ // Add child blocks if the block should contain children but none exists
+ if (block.has_children) {
+ const blockType = block.type;
+ if (blockType in block && !block[blockType].children) {
+ block[blockType].children = childBlocks.find(
+ (x) => x.id === block.id
+ )?.children;
+ }
+ }
+ return block
+ });
+
+ return blocksWithChildren;
+}
diff --git a/dev-example/pages/[id].js b/dev-example/pages/[id].js
index 9cd1ae9..131724d 100644
--- a/dev-example/pages/[id].js
+++ b/dev-example/pages/[id].js
@@ -1,7 +1,7 @@
import React from 'react'
import Head from 'next/head'
import NextImg from 'next/image'
-import { getDatabase, getPage, getBlocks } from '../lib/notion'
+import { getDatabase, getPage, getBlocks, getBlocksWithChildren } from '../lib/notion'
import Link from 'next/link'
import { databaseId } from './blog.js'
@@ -28,6 +28,7 @@ export default function Post({ page, blocks }) {
return