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-here

Then 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"}
TIP

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 TypeRevalidate ValueWhy
Static pages (About, Contact)86400 (24h)Content rarely changes
Blog posts3600 (1h)Webhook handles instant updates
Blog listing3600 (1h)New posts trigger revalidation
Homepage news banner1800 (30min)Should feel fresh
PreviousPhase 4 — FormsNextPhase 6 — Go Headless