Integrations
Next.js — Pages Router
⏱ Setup time
~8 minutes
📦 Adapter
@blogree/nextjs-adapter
✅ Tested on
Next.js 12, 13, 14
Use this guide if your Next.js project uses the pages/ directory (Pages Router). If you're using the app/ directory, see the App Router integration guide instead.
💡
We recommend upgrading to App Router for new projects — it provides better ISR support and the adapter has more capabilities. Both routers are fully supported.
Step 1 — Install the Adapter
npm install @blogree/nextjs-adapter
# .env.local
BLOGREE_API_KEY=bk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
BLOGREE_API_URL=https://api.blogree.com
BLOGREE_WEBHOOK_SECRET=whs_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Step 2 — Create the Webhook API Route
// pages/api/blogree/revalidate.ts
import { createPagesWebhookHandler } from '@blogree/nextjs-adapter';
// Must disable body parser to get raw body for HMAC verification
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 {
// Trigger on-demand ISR revalidation
await res.revalidate('/blog');
await res.revalidate(`/blog/${payload.post.slug}`);
// Handle different event types
if (payload.event === 'post.deleted') {
await res.revalidate('/blog');
}
} catch (err) {
console.error('Revalidation failed:', err);
// Still return 200 so Blogree marks as delivered
// Revalidation failure doesn't mean delivery failure
}
}
);
Step 3 — Set Webhook URL
Your webhook URL for Pages Router is:
https://yourdomain.com/api/blogree/revalidate
Paste this in Blogree Dashboard → Sites → your site → Settings → Webhook URL.
Step 4 — Fetch Posts in Pages
// pages/blog/index.tsx
import { getBlogreePosts } from '@blogree/nextjs-adapter';
import type { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async () => {
const posts = await getBlogreePosts({
apiKey: process.env.BLOGREE_API_KEY!,
apiUrl: process.env.BLOGREE_API_URL!,
});
return {
props: { posts },
revalidate: 3600, // Fallback ISR: revalidate every hour
};
};
export default function BlogPage({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.slug}>
<a href={`/blog/${post.slug}`}>{post.title}</a>
</li>
))}
</ul>
);
}
// pages/blog/[slug].tsx
import { getBlogreePost, getBlogreePosts } from '@blogree/nextjs-adapter';
import type { GetStaticProps, GetStaticPaths } from 'next';
import Head from 'next/head';
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 } })),
fallback: 'blocking', // ISR: generate new pages on demand
};
};
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!,
});
if (!post) return { notFound: true };
return {
props: { post },
revalidate: 3600,
};
};
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>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.body.html }} />
</article>
</>
);
}
ISR vs On-Demand Revalidation
Pages Router supports two revalidation strategies with Blogree:
Faster updates
On-Demand (recommended)
Your webhook handler calls res.revalidate() immediately when Blogree delivers a new post. Pages update within seconds of publishing.
Simpler setup
Time-based ISR (fallback)
Set revalidate: N in getStaticProps. Pages regenerate every N seconds in the background. New posts appear after up to N seconds of delay.