Build Phases
Phase 5 — ISR & Revalidation Webhooks
Fresh content seconds after publishing — no full rebuild needed.
Without ISR: New WordPress content only appears after running vercel build again.
With ISR: New content appears seconds after publishing in wp-admin.
Step 1 — Create the Revalidation Endpoint
// src/app/api/revalidate/route.ts
import { revalidatePath } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const secret = request.nextUrl.searchParams.get("secret");
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ message: "Invalid secret" }, { status: 401 });
}
const { post_type, slug } = await request.json();
if (post_type === "post") {
revalidatePath(`/blog/${slug}`);
revalidatePath("/blog");
} else if (post_type === "projects") {
revalidatePath(`/projects/${slug}`);
revalidatePath("/projects");
}
revalidatePath("/"); // Always revalidate homepage (news banner)
return NextResponse.json({ revalidated: true, post_type, slug });
}Step 2 — Trigger Webhook from WordPress
// functions.php
function trigger_nextjs_revalidation($new_status, $old_status, $post) {
if ($new_status !== 'publish') return;
if (!in_array($post->post_type, ['post', 'projects', 'page'])) return;
$secret = 'your-super-secret-key-here';
$url = 'https://yourdomain.com/api/revalidate?secret=' . $secret;
wp_remote_post($url, array(
'headers' => array('Content-Type' => 'application/json'),
'body' => json_encode(array(
'post_type' => $post->post_type,
'slug' => $post->post_name
)),
'timeout' => 15,
'blocking' => false,
));
}
add_action('transition_post_status', 'trigger_nextjs_revalidation', 10, 3);Step 3 — Add Secret to Vercel
Go to your Vercel project → Settings → Environment Variables and add:
REVALIDATION_SECRET = your-super-secret-key-hereThen redeploy once for the variable to take effect.
Test the Webhook Locally
curl -X POST "http://localhost:3000/api/revalidate?secret=your-secret" \
-H "Content-Type: application/json" \
-d '{"post_type":"post","slug":"hello-world"}'
# Expected response:
# {"revalidated":true,"post_type":"post","slug":"hello-world"}Set blocking => false in the WordPress wp_remote_post call. This means WordPress does not wait for Next.js to respond before returning control to the editor — publish feels instant.
Recommended Revalidation Times
| Page Type | Revalidate Value | Why |
|---|---|---|
| Static pages (About, Contact) | 86400 (24h) | Content rarely changes |
| Blog posts | 3600 (1h) | Webhook handles instant updates |
| Blog listing | 3600 (1h) | New posts trigger revalidation |
| Homepage news banner | 1800 (30min) | Should feel fresh |