Use notion as a CMS for your Next Project
March 26, 2025

The Beginning of My Notion-Powered Portfolio
When I decided to rebuild my portfolio using Next.js (version 2.0), I wanted a content management system that was flexible, developer-friendly, but didn't require complex setup or maintenance. As a developer who already used Notion for personal organization, it seemed like the perfect candidate to serve as my CMS.
The goal was clear: leverage Notion's user-friendly interface for content creation while building a seamless integration with my TypeScript-based Next.js portfolio.
The Architecture: Notion as a Headless CMS
My solution centered around a two-part structure in Notion:
- A metadata database that stores essential information for each blog post:
- Content pages linked to database entries, where each page contains the actual content of a blog post, formatted using Notion's rich text editor.
This separation of concerns gave me both structured data (for listing and previewing content) and rich content (for the actual blog posts).
Visual Architecture Overview
Here's a visual representation of my portfolio's architectur
flowchart TD subgraph Notion DB[Metadata Database] --> Pages[Content Pages] end subgraph NextJS["Next.js Portfolio"] Connector[Notion Connector] --> Cache[Caching Layer] Cache --> ISR[Incremental Static Regeneration] ISR --> Renderer[Content Renderer] Renderer --> NotionInterpreter[Notion Blocks Interpreter] Renderer --> MermaidInterpreter[Mermaid Diagram Interpreter] end subgraph ThirdParty["Third-Party Services"] Resend[Resend Email API] end Notion -->|API| Connector NextJS -->|Contact Form| Resend Resend -->|Notifications| User[Users/Me]
Building the Notion Connector
The first technical challenge was connecting my Next.js application to Notion. I implemented a custom Notion connector using the official Notion API. This connector handles:
- Authentication with Notion
- Fetching database entries for the blog listing
- Retrieving individual page content when a user navigates to a specific post
// Example of my Notion connector setup (simplified)
export class NotionConnector {
private client: Client;
constructor(token: string) {
this.client = new Client({ auth: token });
}
async getBlogPosts() {
const response = await this.client.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
sorts: [{ property: 'Published', direction: 'descending' }]
});
return response.results;
}
async getBlogPost(slug: string) {
// Query the database for the matching slug
// Then get the linked page content
}
}
Optimizing Performance with Caching and ISR
Performance was a priority for me. To ensure fast loading times without constantly hitting the Notion API, I implemented:
- Server-side caching to store API responses and reduce redundant calls
- Incremental Static Regeneration (ISR) in Next.js, which allowed me to:
This approach gave my portfolio the speed of a static site with the freshness of dynamic content:
// Example of ISR implementation in a page
export async function getStaticProps({ params }) {
const { slug } = params;
const post = await notionConnector.getBlogPost(slug);
return {
props: { post },
revalidate: 3600 // Regenerate pages hourly
};
}
export async function getStaticPaths() {
const posts = await notionConnector.getBlogPosts();
const paths = posts.map(post => ({ params: { slug: post.properties.Slug.rich_text[0].plain_text } }));
return {
paths,
fallback: 'blocking'
};
}
The Notion Interpreter: Rendering Rich Content
One of the more challenging aspects was transforming Notion's block-based content into properly styled HTML for my portfolio. I built a custom Notion interpreter that:
- Parses the JSON response from Notion's API
- Maps each block type to the appropriate React component
- Maintains proper nesting and relationships between blocks
- Preserves styling and formatting from Notion
My interpreter handles everything from basic text formatting to complex elements like code blocks, tables, and images.
The Mermaid Integration: Visualizing with Diagrams
As a technical writer, I often need to create diagrams to explain concepts. Notion's native diagramming capabilities are limited, so I implemented a custom Mermaid interpreter:
const MermaidBlock = ({ content }) => {
useEffect(() => {
if (typeof window !== 'undefined') {
import('mermaid').then(mermaid => {
mermaid.initialize({ startOnLoad: true });
mermaid.contentLoaded();
});
}
}, [content]);
return (
<div className="mermaid-wrapper">
<pre className="mermaid">
{content}
</pre>
</div>
);
};
This allowed me to write diagram code in Notion code blocks with the "mermaid" language tag, which my portfolio renders as beautiful, interactive diagrams.
Challenges and Solutions
- API Rate Limits: Notion's API has rate limits that could affect site performance during traffic spikes. My caching layer mitigated this by reducing the number of API calls needed.
- Content Synchronization: Determining when to refresh cached content was tricky. I implemented a hybrid approach where most content uses ISR with a reasonable revalidation period, while critical updates trigger manual revalidation.
- Complex Block Rendering: Some Notion blocks have intricate structures with nested content. Building a robust interpreter required careful handling of these edge cases to ensure the rendered output matched the Notion experience.
The Result: A Developer-Friendly Content Workflow
The completed system gives me the best of both worlds:
- For writing: I use Notion's familiar interface to draft, edit, and publish content with rich formatting, images, and even diagrams.
- For development: My Next.js application pulls this content via the API, renders it beautifully, and serves it with optimal performance.
- For communication: Resend handles all email-related functionality, ensuring reliable delivery of messages and notifications.
This setup has dramatically improved my content creation workflow. I can focus on writing without switching contexts to code editors or deployment tools. Once content is ready in Notion, it automatically appears on my portfolio site after the next revalidation cycle.
Conclusion: The Future of My Portfolio CMS
Building this custom Notion-powered CMS has been an enlightening journey that balanced technical challenges with practical benefits. The system is extensible enough that I can add new content types beyond blog posts as my portfolio evolves.
For developers considering a similar approach, I highly recommend exploring Notion as a headless CMS option. The combination of user-friendly content creation and powerful API capabilities makes it an excellent choice for personal sites and portfolios.