HomeTechReact
Tech

React

Kunal Arya
Kunal Arya
May 4, 2025

A good tutorial for the next js where you will get to know about hoe the things is working behind the sences/

hellpnextreact
React
3 min
Featured
Author
Kunal Arya
Date
May 4, 2025
Description
A good tutorial for the next js where you will get to know about hoe the things is working behind the sences/
Featured
no
Published
Published
ReadTime
3 min
category
Tech
created at
May 4, 2025
slug
react
tag
hellp
next
react

Building a Next.js Blog with Notion as a Database

This guide walks you through creating a blog using Next.js as the frontend and Notion as your content database, with automatic updates when your Notion pages change.

Prerequisites

  • Node.js installed (v16.8.0 or higher)
  • A Notion account
  • Basic knowledge of React and JavaScript

Step 1: Set up your Notion database

  1. Create a new Notion database for your blog posts:
      • Create a new page in Notion
      • Create a new database (full page or inline)
      • Add the following properties to your database:
        • Title (type: Title)
        • Slug (type: Text)
        • Published (type: Checkbox)
        • Date (type: Date)
        • Tags (type: Multi-select)
        • Description (type: Text)
  1. Set up Notion API access:
      • Create a new integration
      • Copy your integration token (keep this secure)
      • Share your database with your integration (click "Share" on your database and add your integration)
      • Copy your database ID (from the URL, it's the part after the workspace name and before the question mark)

Step 2: Create your Next.js project

  1. Initialize a new Next.js project:
    1. npx create-next-app@latest my-notion-blog cd my-notion-blog
  1. Install required dependencies:
    1. npm install @notionhq/client notion-to-md react-markdown

Step 3: Configure environment variables

Create a .env.local file in your project root:
NOTION_API_KEY=your_integration_token NOTION_DATABASE_ID=your_database_id REVALIDATE_SECRET=random_string_for_security

Step 4: Set up Notion client

Create a new file lib/notion.js:
import { Client } from "@notionhq/client"; import { NotionToMarkdown } from "notion-to-md"; const notion = new Client({ auth: process.env.NOTION_API_KEY, }); const n2m = new NotionToMarkdown({ notionClient: notion }); export async function getAllPosts() { const databaseId = process.env.NOTION_DATABASE_ID; const response = await notion.databases.query({ database_id: databaseId, filter: { property: "Published", checkbox: { equals: true, }, }, sorts: [ { property: "Date", direction: "descending", }, ], }); return response.results.map((page) => { return { id: page.id, title: page.properties.Title.title[0]?.plain_text || "Untitled", slug: page.properties.Slug.rich_text[0]?.plain_text || "", date: page.properties.Date.date?.start || "", tags: page.properties.Tags.multi_select.map((tag) => tag.name), description: page.properties.Description.rich_text[0]?.plain_text || "", }; }); } export async function getPostBySlug(slug) { const databaseId = process.env.NOTION_DATABASE_ID; const response = await notion.databases.query({ database_id: databaseId, filter: { property: "Slug", rich_text: { equals: slug, }, }, }); if (response.results.length === 0) { return null; } const page = response.results[0]; const mdBlocks = await n2m.pageToMarkdown(page.id); const markdown = n2m.toMarkdownString(mdBlocks); return { id: page.id, title: page.properties.Title.title[0]?.plain_text || "Untitled", slug: page.properties.Slug.rich_text[0]?.plain_text || "", date: page.properties.Date.date?.start || "", tags: page.properties.Tags.multi_select.map((tag) => tag.name), description: page.properties.Description.rich_text[0]?.plain_text || "", content: markdown.parent, }; }

Step 5: Create pages and components

1. Home Page (app/page.js)

import Link from "next/link"; import { getAllPosts } from "@/lib/notion"; export const revalidate = 60; // Revalidate this page every 60 seconds export default async function Home() { const posts = await getAllPosts(); return ( <main className="container mx-auto px-4 py-8"> <h1 className="text-3xl font-bold mb-8">My Blog</h1> <div className="grid gap-6"> {posts.map((post) => ( <article key={post.id} className="border p-4 rounded-lg"> <h2 className="text-xl font-bold"> <Link href={`/blog/${post.slug}`}>{post.title}</Link> </h2> <div className="text-gray-500 text-sm mt-1">{new Date(post.date).toLocaleDateString()}</div> {post.tags.length > 0 && ( <div className="flex gap-2 mt-2"> {post.tags.map((tag) => ( <span key={tag} className="bg-gray-100 text-xs px-2 py-1 rounded"> {tag} </span> ))} </div> )} <p className="mt-2">{post.description}</p> </article> ))} </div> </main> ); }

2. Blog Post Page (app/blog/[slug]/page.js)

import { getPostBySlug, getAllPosts } from "@/lib/notion"; import ReactMarkdown from "react-markdown"; import Link from "next/link"; import { notFound } from "next/navigation"; export const revalidate = 60; // Revalidate this page every 60 seconds export async function generateStaticParams() { const posts = await getAllPosts(); return posts.map((post) => ({ slug: post.slug })); } export default async function BlogPost({ params }) { const post = await getPostBySlug(params.slug); if (!post) { notFound(); } return ( <main className="container mx-auto px-4 py-8"> <Link href="/" className="text-blue-500 mb-6 block">← Back to all posts</Link> <article> <h1 className="text-3xl font-bold mb-2">{post.title}</h1> <div className="text-gray-500 mb-4">{new Date(post.date).toLocaleDateString()}</div> {post.tags.length > 0 && ( <div className="flex gap-2 mb-6"> {post.tags.map((tag) => ( <span key={tag} className="bg-gray-100 text-xs px-2 py-1 rounded"> {tag} </span> ))} </div> )} <div className="prose max-w-none"> <ReactMarkdown>{post.content}</ReactMarkdown> </div> </article> </main> ); }

Step 6: Set up on-demand revalidation

Create a new file app/api/revalidate/route.js:
import { revalidatePath } from "next/cache"; import { NextResponse } from "next/server"; export async function POST(request) { const requestData = await request.json(); const { path, secret } = requestData; // Check for secret to confirm this is a valid request if (secret !== process.env.REVALIDATE_SECRET) { return NextResponse.json({ message: "Invalid token" }, { status: 401 }); } try { // This will revalidate the specified path if (path) { revalidatePath(path); return NextResponse.json({ revalidated: true, path }); } // If no path is provided, revalidate the homepage and all blog posts revalidatePath("/"); revalidatePath("/blog"); return NextResponse.json({ revalidated: true, path: "all" }); } catch (err) { // If there was an error, Next.js will continue // to show the last successfully generated page return NextResponse.json({ message: "Error revalidating" }, { status: 500 }); } }

Step 7: Set up Notion webhook (optional)

For real-time updates when Notion content changes, you can use Notion's "page_updated" webhooks or a third-party service like Pipedream or n8n to trigger your revalidation endpoint.
If you're using Pipedream, create a workflow that:
  1. Triggers on Notion page updates (using their Notion integration)
  1. Sends a POST request to your revalidation endpoint: with the body:
    1. <https://your-site.com/api/revalidate>
      { "secret": "your_revalidate_secret", "path": "/" }

Step 8: Deploy your application

You can deploy your Next.js application to platforms like Vercel or Netlify:
  1. Push your code to a GitHub repository
  1. Connect your repository to Vercel or Netlify
  1. Add your environment variables to the deployment platform
  1. Deploy your application

Conclusion

You now have a fully functional blog that uses Notion as a CMS and Next.js as the frontend. When you update content in Notion, your website will automatically refresh with the new content.
  • For immediate updates: Use the webhook approach
  • For scheduled updates: Rely on the revalidation timing you've set (60 seconds in our example)

Additional Improvements

  1. Add image support: Enhance the markdown renderer to handle Notion images
  1. Create tag filters: Add pages to filter posts by tags
  1. Add pagination: Implement pagination for the blog list
  1. Add search functionality: Create a search feature for your blog posts
  1. Custom styling: Improve the design with Tailwind CSS or another styling solution

About

Kunal Arya

Kunal Arya

Web Developer

A good tutorial for the next js where you will get to know about hoe the things is working behind the sences/

📌 India

AI in Business Management: Improving Efficiency and Decision Making
MANAGEMENT

AI in Business Management: Improving Efficiency and Decision Making

Ethan Caldwell on July 7, 2024

The Future of Remote Work: Trends and Best Practices
TECHNOLOGY

The Future of Remote Work: Trends and Best Practices

Sarah Johnson on August 12, 2024

Sustainable Business Models for the Modern Economy
INNOVATION

Sustainable Business Models for the Modern Economy

Alex Rivera on September 3, 2024

Kunal Arya

Developer, builder, and problem-solver with a passion for clean code, product thinking, and scalable tech..

© 2024 Kunal Arya. All Rights Reserved.