• Home
  • Blog
  • Stack

© 2026 YIKZERO

Notes on Calling the Notion API with Next.js

Develop· October 4, 2023
This article was translated from Chinese with GPT-5.5. If there are any discrepancies, please refer to the original Chinese version.

Pitfall Collection

The pitfalls come partly from Next.js and partly from the Notion API. Of course, I myself am the biggest pitfall.

  1. Next.js server-side rendering
  2. Notion API data filtering
  3. Notion Block data rendering
  4. Notion temporary image expiration
  5. Using the Next.js Image component
  6. Rendering styles for Notion article pages

The above is basically all the pitfalls I ran into. Before introducing my experience dealing with them, let me briefly talk about the original intention of the project.

Original Intention

At first, it was because I had learned frontend development and then wanted to understand backend development. Initially, I made a full-stack text-to-speech project based on edge-tts called Rotts (frontend React + backend Flask). But this project was very simple: the backend basically just requested the API, obtained the voice list, then passed information such as voice, pitch, and text to edge-tts, waited for the audio to return, and then played/downloaded it. I felt like the backend part was over before I had really experienced much of it.

So I wanted to find another project to continue learning backend development. The first thing that came to mind was Node.js. During my research, I saw many people recommending Next.js, which is also based on Node.js. So it clicked immediately. I also wanted to simply reinvent the wheel and build a blog system that uses Notion as a CMS. Visually, everything would be kept simple to reduce burden, with a focus on content rendering.

Originally, I thought that with ChatGPT, I could just ask it whenever I didn’t know something. As it turned out, the Next.js App Router was too new, and it wasn’t even in its knowledge base. Every time I asked it related questions, the answers were not accurate enough.

Project Information

  • Name: Brog
  • Introduction: A minimalist blog system built with Next.js, using Notion as the CMS
  • Tech stack: React + Next.js + Typescript + TailwindCSS
  • Github: https://github.com/yikZero/Brog

Next.js Pitfalls

Duplicate Requests

Actually, when I first started fetching data, I had doubts. I fetch here, then fetch there. It’s all the same data—wouldn’t that cause multiple requests and be very wasteful?

Because of this, I kept trying to separate function logic from requests and just pass several props around... But in fact, at least in React, you don’t need to worry about this.

Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.

If the link and parameters you fetch remain unchanged, then even if you make multiple requests, React will help merge them into a single request. So there’s no need to keep forwarding props between components. Just request the same data multiple times; it’s not a problem. React will merge the requests on its own and won’t repeatedly make duplicate requests that affect the network.

Server Components

What counts as a server component? Anything placed under the app/ directory should belong to server components. When I wasn’t sure whether something was a server component, I threw all the components and lib directories I used into app/... just for server components.

If you want it to become a client component, add use client at the top, and it will be recognized as a client component. The official Next.js recommendation is to use server components whenever possible.

next/image Usage

Because of LCP performance, Next.js officially optimizes the usage of the img element and recommends using the official Image component.

For specific usage, refer to Image Optimization

I thought it would be simple—just replace the name. But it requires me to provide explicit height and width. For images I fetch dynamically, how would I know the image height and width? So I used a bit of a workaround... also a solution from stackoverflow.

<figure className="relative">
  <image
    src="{src}"
    width="{0}"
    height="{0}"
    sizes="100vw"
    className="w-full h-auto"
  />
</figure>

Notion-Client Pitfalls

Notion API
Notion API

Data Fetching

Notion officially provides @notionhq/client, and react-notion-x also has a notion-client. I learned about notion-client first, so after finishing the styles, I tried integrating the third-party implementation.

How should I put it? notion-client is also quite good. By providing a public NOTION_PAGE_ID environment variable, you can fetch the data. But what you get is the full dataset, and there is no way to filter by field name like with the official API, so organizing the data becomes much more troublesome. For a beginner like me, this was way too much trouble... After all, the data obtained from Notion is nested layer by layer, and every block has its own ID, which made me completely dizzy.

In the end, I still referred to the official API usage in notion-blog-nextjs to obtain the corresponding data.

Image Expiration

The image links in the data officially provided by Notion are temporary Amazon links, which expire after one hour. This causes the images to break.

Vercel Cron: Many people also choose to use the Vercel cron feature to rebuild and render every hour. But I think this wastes too many resources and is too environmentally unfriendly (:

Converting temporary addresses into permanent addresses: For this solution, you can refer to fix: solve linked img crash #524

Uploading to Upyun via PicGo: Because I have migrated data before, if all Markdown images use relative paths, it is simply a disaster. So now I choose to upload them to Upyun via PicGo and return my CDN address. If I need to migrate, at least the images won’t be lost.

A Study of Modular UI Design: Icons
HomeLab Environment Setup