@blogree/nextjs-adapter
v1.0.3
Published
Official Next.js adapter for Blogree — pull posts and handle ISR revalidation
Maintainers
Readme
@blogree/nextjs-adapter
The official Next.js adapter for Blogree. Blogree is an AI-powered content engine that helps you generate thousands of SEO-optimized articles and sync them directly to your website. Connect your website to your Blogree workspace in minutes. It automatically finds the gaps in your content and generates high-quality, intent-driven articles to fill those gaps. Fetch your blog posts, handle automatic caching (ISR), and set up secure webhooks with almost zero configuration.
🚀 5-Minute Integration Guide
If you just want to get your blog up and running quickly, follow these steps. The code below is designed so you can simply copy and paste it into your project.
Step 1: Install the package
npm install @blogree/nextjs-adapterOr with yarn:
yarn add @blogree/nextjs-adapterOr with pnpm:
pnpm add @blogree/nextjs-adapterStep 2: Add your Environment Variables
- Log in to your Blogree Dashboard
- Navigate to Sites, and add your website there
- Copy:
- Pull API Key (for fetching posts)
- Webhook Secret (for signature verification)
- Add them to your
.env.localfile:
BLOGREE_API_KEY=your_pull_api_key
BLOGREE_WEBHOOK_SECRET=your_webhook_secretStep 3: Fetch and Display Posts
We provide a simple helper function getBlogreePosts to fetch your content. Here is a perfect boilerplate for your core blog page using the modern Next.js App Router:
// app/blog/page.tsx
import { getBlogreePosts } from '@blogree/nextjs-adapter';
import Link from 'next/link';
export default async function BlogPage() {
// 1. Fetch your posts from Blogree
const posts = await getBlogreePosts({
apiKey: process.env.BLOGREE_API_KEY!,
});
// 2. Render them to the screen
return (
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
<h1>My Blog</h1>
{posts.map((post) => (
<article key={post.id} style={{ marginBottom: '2rem' }}>
{/* Optional: display author */}
{post.author && (
<p style={{ fontSize: '13px', color: '#6b7280' }}>By {post.author.name}</p>
)}
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<Link href={`/blog/${post.slug}`}>
<span style={{ color: 'blue', textDecoration: 'underline' }}>Read more</span>
</Link>
</article>
))}
</div>
);
}Step 4: Show a Single Post
When a user clicks "Read more", they need to go to a dedicated page that displays the content of your post. We use the helper function getBlogreePost for this.
// app/blog/[slug]/page.tsx
import { getBlogreePost } from '@blogree/nextjs-adapter';
import { notFound } from 'next/navigation';
export default async function BlogPostPage({ params }: { params: { slug: string } }) {
// 1. Fetch a single post based on the URL parameter (slug)
const post = await getBlogreePost(params.slug, {
apiKey: process.env.BLOGREE_API_KEY!,
});
// 2. Show a graceful 404 error if someone types a bad URL structure
if (!post) {
notFound();
}
// 3. Render the post HTML
return (
<article style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
<h1>{post.title}</h1>
<time>{new Date(post.published_at).toLocaleDateString()}</time>
{/* Optional: display the author block */}
{post.author && (
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', margin: '16px 0', padding: '12px', background: '#f9fafb', borderRadius: '8px' }}>
{post.author.avatar_url && (
<img src={post.author.avatar_url} alt={post.author.name} style={{ width: '40px', height: '40px', borderRadius: '50%' }} />
)}
<div>
<strong>{post.author.name}</strong>
{post.author.bio && <p style={{ margin: 0, fontSize: '13px', color: '#6b7280' }}>{post.author.bio}</p>}
</div>
</div>
)}
<div
style={{ marginTop: '2rem' }}
dangerouslySetInnerHTML={{ __html: post.body.html }}
/>
</article>
);
}Step 5: Setup Auto-Updating (Zero-Config Webhooks)
By default, Next.js caches your pages so they load blazingly fast. But when you write a new post in the Blogree dashboard, Next.js needs to know about it.
We provide a zero-config webhook handler that securely validates the request and automatically clears the proper cache tags for you when you hit "Publish".
First, create an API Route in Next.js:
// app/api/blogree/route.ts
import { createWebhookHandler } from '@blogree/nextjs-adapter';
// It really is this simple!
export const POST = createWebhookHandler({
apiKey: process.env.BLOGREE_API_KEY!,
webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET!,
});Next, connect it to your Blogree account:
Go to your Blogree Dashboard > Sites > Your Site > Webhook URL and add your live URL (e.g., https://yourwebsite.com/api/blogree). Select all post events.
🎉 That's it! You've successfully integrated a fully-featured, auto-updating, SEO-friendly blog.
Types
If you are strictly typing your components, import our standard types:
import type { BlogreePost, WebhookPayload, BlogreeConfig } from '@blogree/nextjs-adapter';
interface BlogreePost {
id: string;
slug: string;
title: string;
excerpt: string | null;
body: {
html: string;
markdown: string;
json: Record<string, unknown> | null;
};
meta: {
title: string | null;
description: string | null;
og_image: string | null;
};
tags: string[];
published_at: string;
status: string;
version: number;
// v1.0.2: Author attribution support
author?: {
name: string;
slug: string;
bio: string | null;
avatar_url: string | null;
} | null;
}Author Attribution (New in v1.0.2)
Each post now optionally includes an author object when an author has been assigned in the Blogree dashboard. You can use this to render author bylines, bios, and profile images anywhere in your blog.
{post.author && (
<div className="author-card">
{post.author.avatar_url && <img src={post.author.avatar_url} alt={post.author.name} />}
<span>By {post.author.name}</span>
{post.author.bio && <p>{post.author.bio}</p>}
</div>
)}Advanced Client Class
Instead of the helper functions from Step 3, if your app is more advanced, you can instantiate our BlogreeClient class once and reuse it across files. Notice that apiUrl is completely optional for local development overrides!
import { BlogreeClient } from '@blogree/nextjs-adapter';
const client = new BlogreeClient({
apiKey: process.env.BLOGREE_API_KEY!,
webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET!,
// apiUrl: 'http://localhost:4000' // Override for local testing only
});
// Fetch posts
const posts = await client.getPosts({ limit: 10, page: 1, tag: 'technology' });
// Fetch a single post
const post = await client.getPost('my-post-slug');
// Manually confirm a delivery status back to Blogree
await client.confirmDelivery(postId, 'success');
await client.confirmDelivery(postId, 'failed', 'Rendering error occurred');Manual Delivery Confirmation
The confirmDelivery() method lets you manually report the status of a post delivery back to your Blogree dashboard. This is useful when you have custom webhook logic and want Blogree's analytics to accurately reflect delivery success or failure.
// Inside a custom webhook handler:
await client.confirmDelivery(payload.post.id, 'success');
// Or on failure:
await client.confirmDelivery(payload.post.id, 'failed', 'Database write failed');If you are using the older Next.js pages/ directory instead of the app/ router, use our dedicated pages router handler. Manual revalidation requires providing paths.
Webhook Handler for Pages Router:
// pages/api/blogree/webhooks.ts
import { createPagesWebhookHandler } from '@blogree/nextjs-adapter';
export default createPagesWebhookHandler(
{
apiKey: process.env.BLOGREE_API_KEY!,
webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET!,
},
async (payload, res) => {
// Manual ISR revalidation
await res.revalidate('/blog');
await res.revalidate(`/blog/${payload.post.slug}`);
}
);- "Posts aren't appearing on my live production site."
Make sure you have set the environment variables in your hosting provider's dashboard (like Vercel or Netlify), not just locally in
.env.local. - "Webhooks are failing or returning Unauthorized error 401."
Double check that your
BLOGREE_WEBHOOK_SECRETmatches exactly what is running in your Blogree dashboard. The adapter strictly verifies the HMAC-SHA256 signature to protect your site. - "The
authorfield is alwaysnull." Authors must be created and assigned to a post inside the Blogree Dashboard. Navigate to Posts → Edit Post → Author to assign one. - "TypeScript errors with next/cache"
Ensure your
tsconfig.jsonincludes the correct libs:"lib": ["ES2020", "DOM", "DOM.Iterable"]
Still stuck? View Documentation • Report an Issue
