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.