Sanity x Next.js revalidate tag 動態更新 post (ISR)

2025/09/09

使用 Sanity 的 webhook 功能 revalidate Next 的靜態頁面達成 ISR(Incremental Static Regeneration)
Sanity x Next.js revalidate tag 動態更新 post (ISR)

RSC

首先在RSC取得post data 然後傳給 client component,請注意sanity 的 fetch function 除了query之外還要加上兩個參數 (變數, Next.js 資料快取選項)

tsx
1// src/app/[lng]/(home)/_components/PostsSection.tsx  ← Server Component(不要 "use client")
2import Grid from "@mui/material/Grid";
3import PostCards from "@/components/UI/PostCards";
4import { client } from "@/sanity/lib/client";
5import { PostDoc } from "@/schema/type/post";
6
7export default async function PostsSection() {
8  // SSR 取得文章資料
9  const posts = await client.fetch<PostDoc[]>(
10    `*[_type == "post"] | order(_createdAt desc) {
11      _id,
12      _createdAt,
13      title,
14      description,
15      photo,
16      "slug": slug.current,
17      categories[]->{
18        _id,
19        title,
20        "slug": slug.current
21      },
22      author->{
23        _id,
24        name,
25        "slug": slug.current,
26        email,
27        avatar
28      }
29    }`,
30    {}, // 這邊查詢所有post,不設定變數
31    { next: { tags: ["posts"] } }
32  );
33  return (
34    <Grid container spacing={2} columns={12}>
35      <PostCards posts={posts} />
36    </Grid>
37  );
38}
39

Route Handler

設定revalidate tag同sanity fetch 的 tag,並且為了防止濫用可加上一個REVALIDATE_SECRET code (可選)

tsx
1// app/api/posts/revalidate
2import { NextRequest, NextResponse } from "next/server";
3import { revalidateTag } from "next/cache";
4
5export async function POST(req: NextRequest) {
6  const secret = req.nextUrl.searchParams.get("secret");
7  if (secret !== process.env.REVALIDATE_SECRET) {
8    return NextResponse.json({ error: "Invalid secret" }, { status: 401 });
9  }
10  console.log("Revalidating posts");
11  revalidateTag("posts");
12  return NextResponse.json({ revalidated: true, now: Date.now() });
13}
14

Sanity 後台

到後台設定webhook 的觸發條件、觸發的api route。

設定webhook,免費版本只有兩個可用,剛好一個測試一個deploy用,以測試的來說明。

後台設定 webhook

URL 需要注意無法用localhost:3000 所以要使用 Cloudflare Tunnel 來讓 Sanity Webhook 可以直接存取的本機 API

設定觸發條件:

Filter 代表哪些type會觸發條件,這邊設定 post、category、author 更新時會觸發

Projection 代表發送時 payload(內容)要帶哪些欄位

Dev Terminal

如果設定成功就能看到 log