Next.js — Pages Router
Welcome! Use this guide if your Next.js project uses the traditional pages/ directory. If your project uses the newer app/ directory, please see the App Router integration guide instead.
Estimated setup time: 8 minutes
Tip: We recommend upgrading to App Router for new projects, as it provides better caching support. However, both routers are fully supported by Blogree!
1. Install the Adapter
First, we need to add the official Blogree toolkit to your project. Open your computer's terminal (or the terminal panel built into VS Code), make sure you are inside your project folder, and run this command:
npm install @blogree/nextjs-adapter2. Setup Your Secret Passwords (Environment Variables)
Your website needs secure keys to talk to Blogree. We store these in a hidden file so they don't accidentally get shared.
- Go to the very main folder of your project (the root directory) and create a new file named exactly
.env.local. - Next, go to your Blogree dashboard by visiting app.blogree.com.
- Navigate to Sites, click on your site, and go to the Settings tab to find your API keys.
- Copy those keys and paste them into your new
.env.localfile like this:
BLOGREE_API_KEY=bk_live_xxxxxxxxxxxxxxxxxxxx
BLOGREE_API_URL=https://api.blogree.com
BLOGREE_WEBHOOK_SECRET=whs_xxxxxxxxxxxxxxxxxxxx2. Create the Webhook (The Auto-Updater)
When you click "Publish" in Blogree, Blogree will send a hidden signal (a webhook) to your website so it can update immediately. Let's create the file that listens for that signal.
In your project, go to the pages folder, create an api folder, then a blogree folder, and finally create a file named revalidate.ts.
import { createPagesWebhookHandler } from '@blogree/nextjs-adapter';
// CRITICAL: We must disable the default Next.js body parser.
// This allows the adapter to read the raw text and verify Blogree's security signature.
export const config = {
api: {
bodyParser: false,
},
};
export default createPagesWebhookHandler(
{
apiKey: process.env.BLOGREE_API_KEY!,
apiUrl: process.env.BLOGREE_API_URL!,
webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET!,
},
async (payload, res) => {
try {
// This forces Next.js to update the main blog page
await res.revalidate('/blog');
// This forces Next.js to update the specific article you just published
await res.revalidate(`/blog/${payload.post.slug}`);
if (payload.event === 'post.deleted') {
await res.revalidate('/blog');
}
} catch (err) {
console.error('Revalidation failed:', err);
}
}
);3. Connect the Webhook in the Dashboard
Now that your code is ready to listen, tell Blogree where to send the signal.
- Go to app.blogree.com.
- Navigate to Sites → select your site → go to Settings.
- In the Webhook URL field, paste your new API route:
https://your-website.com/api/blogree/revalidate4. Show the Blog List
Let's build the page that lists all of your articles. In Pages Router, we use a special function called getStaticProps to fetch data before the page loads.
Create a file at pages/blog/index.tsx and paste this code:
import { getBlogreePosts } from '@blogree/nextjs-adapter';
import type { GetStaticProps } from 'next';
import Link from 'next/link';
// 1. Fetch the data from Blogree securely on the server
export const getStaticProps: GetStaticProps = async () => {
const posts = await getBlogreePosts({
apiKey: process.env.BLOGREE_API_KEY!,
apiUrl: process.env.BLOGREE_API_URL!,
});
return {
props: { posts },
// Rebuild the page automatically every 1 hour just in case
revalidate: 3600,
};
};
// 2. Display the data to the user
export default function BlogPage({ posts }) {
return (
<main style={{ padding: '2rem' }}>
<h1>Latest Articles</h1>
<ul>
{posts.map(post => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>
{post.title}
</Link>
</li>
))}
</ul>
</main>
);
}5. Show the Individual Article
Finally, when a user clicks an article, they need a page to read it. Because you don't want to make a new file for every single blog post, we use a dynamic file name: [slug].tsx.
Create a file at pages/blog/[slug].tsx and paste this code:
import { getBlogreePost, getBlogreePosts } from '@blogree/nextjs-adapter';
import type { GetStaticProps, GetStaticPaths } from 'next';
import Head from 'next/head';
// 1. Tell Next.js which URLs to generate based on your current posts
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await getBlogreePosts({
apiKey: process.env.BLOGREE_API_KEY!,
apiUrl: process.env.BLOGREE_API_URL!,
});
return {
paths: posts.map(p => ({ params: { slug: p.slug } })),
// 'blocking' ensures that if you publish a NEW post, Next.js generates
// the page the first time someone visits it.
fallback: 'blocking',
};
};
// 2. Fetch the content for the specific article requested
export const getStaticProps: GetStaticProps = async ({ params }) => {
const post = await getBlogreePost({
slug: params!.slug as string,
apiKey: process.env.BLOGREE_API_KEY!,
apiUrl: process.env.BLOGREE_API_URL!,
});
// Show a 404 error if the post was deleted or doesn't exist
if (!post) return { notFound: true };
return {
props: { post },
revalidate: 3600,
};
};
// 3. Render the article page
export default function BlogPostPage({ post }) {
return (
<>
<Head>
<title>{post.meta.title}</title>
<meta name="description" content={post.meta.description} />
<meta property="og:image" content={post.meta.og_image} />
</Head>
<article style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
<h1>{post.title}</h1>
{/* Render the HTML from Blogree */}
<div dangerouslySetInnerHTML={{ __html: post.body.html }} />
</article>
</>
);
}Understanding Revalidation (How Updates Work)
Next.js Pages Router gives you two ways to keep your website up to date with Blogree. We designed this guide to use the best of both!
1. On-Demand (Recommended)
Faster updates: This is what Step 2 handles. Your webhook handler tells your server to rebuild exactly one page immediately when Blogree delivers a new post. Your website updates within seconds of hitting "Publish."
2. Time-based ISR (Fallback)
Simpler setup: This is the revalidate: 3600 you added in Steps 4 and 5. It tells Next.js to automatically check for updates every hour (3600 seconds) in the background. It acts as a safety net in case a webhook signal gets lost.