Getting started
Render a Notion page in a Next.js App Router project.
1. Create an integration
Create an internal integration and copy its token. Then share your page or database with it: open it in Notion, ⋯ → Connections, and add the integration.
NOTION_TOKEN=ntn_...2. Install
npm install @yokinist/notion-fetch @yokinist/notion-rsc @notionhq/client3. Render a page
import { Client } from "@notionhq/client";
import { createRequestLimiter, createTimeoutFetch } from "@yokinist/notion-fetch";
export const notion = new Client({
auth: process.env.NOTION_TOKEN,
fetch: createTimeoutFetch(), // aborts requests that stall
});
export const limit = createRequestLimiter();import { fetchBlockTree } from "@yokinist/notion-fetch";
import { NotionBlocks } from "@yokinist/notion-rsc";
import { limit, notion } from "../notion";
export const revalidate = 60;
export default async function Page({ params }: PageProps<"/[id]">) {
const { id } = await params;
const blocks = await fetchBlockTree(notion, id, { limit });
return (
<article className="prose">
<NotionBlocks
blocks={blocks}
fileUrl={(blockId) => `/api/notion/file/${blockId}`}
/>
</article>
);
}import "@yokinist/notion-rsc/styles.css";4. Serve uploaded files
Notion's URLs for uploaded files expire after an hour. Add a route that redirects to a fresh one, so cached pages keep their images:
import { createFileHandler } from "@yokinist/notion-fetch";
import { limit, notion } from "../../../../notion";
export const GET = createFileHandler({ notion, limit });That's it. See the demo for what each block looks like.
Not using Next.js?
notion-rsc needs only React 19 Server Components, so it works in React Router, TanStack Start and other RSC frameworks; see the examples. createFileHandler takes a Web-standard Request and returns a Response, so it works with Hono, Cloudflare Workers and the like.