コンテンツへスキップ
アプリケーションの構築データ取得Incremental Static Regeneration (ISR)

Incremental Static Regeneration (ISR)

Incremental Static Regeneration (ISR) を使用すると、以下が可能になります。

  • サイト全体を再構築することなく静的コンテンツを更新
  • ほとんどのリクエストに対してプリレンダリングされた静的ページを提供することで、サーバー負荷を軽減
  • 適切な cache-control ヘッダーがページに自動的に追加されることを保証
  • 長い next build 時間をかけることなく、大量のコンテンツページを処理

以下は最小限の例です

app/blog/[id]/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
export const revalidate = 60
 
// We'll prerender only the params from `generateStaticParams` at build time.
// If a request comes in for a path that hasn't been generated,
// Next.js will server-render the page on-demand.
export const dynamicParams = true // or false, to 404 on unknown paths
 
export async function generateStaticParams() {
  const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  return posts.map((post) => ({
    id: String(post.id),
  }))
}
 
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
    (res) => res.json()
  )
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

この例の動作は次のとおりです

  1. next build 中に、既知のすべてのブログ投稿が生成されます(この例では25件)
  2. これらのページ(例:/blog/1)に対するすべてのリクエストはキャッシュされ、即座に表示されます
  3. 60秒が経過した後も、次のリクエストではキャッシュされた(古い)ページが表示されます
  4. キャッシュは無効化され、ページの新しいバージョンがバックグラウンドで生成され始めます
  5. 正常に生成されると、Next.jsは更新されたページを表示し、キャッシュします
  6. /blog/26 がリクエストされた場合、Next.jsはこのページをオンデマンドで生成し、キャッシュします

リファレンス

ルートセグメント設定

関数

時間ベースの再検証

これは/blogにブログ投稿のリストをフェッチして表示します。1時間後、このページのキャッシュは次のページアクセス時に無効化されます。その後、バックグラウンドで最新のブログ投稿を含む新しいバージョンのページが生成されます。

app/blog/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
export const revalidate = 3600 // invalidate every hour
 
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts: Post[] = await data.json()
  return (
    <main>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  )
}

revalidation時間は長く設定することをお勧めします。例えば、1秒ではなく1時間に設定してください。より高い精度が必要な場合は、オンデマンド再検証の使用を検討してください。リアルタイムデータが必要な場合は、動的レンダリングへの切り替えを検討してください。

revalidatePathによるオンデマンド再検証

より正確な再検証の方法として、revalidatePath関数を使用してオンデマンドでページを無効化します。

例えば、新しい投稿を追加した後、このServer Actionが呼び出されます。Server Componentでfetchを使用するかデータベースに接続するかにかかわらず、これによりルート全体のキャッシュがクリアされ、Server Componentは新しいデータをフェッチできるようになります。

app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // Invalidate the /posts route in the cache
  revalidatePath('/posts')
}

デモを見るソースコードを探る

revalidateTagによるオンデマンド再検証

ほとんどのユースケースでは、パス全体の再検証を優先してください。よりきめ細かな制御が必要な場合は、revalidateTag関数を使用できます。たとえば、個々のfetch呼び出しにタグを付けることができます。

app/blog/page.tsx
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog', {
    next: { tags: ['posts'] },
  })
  const posts = await data.json()
  // ...
}

ORMを使用している場合やデータベースに接続している場合は、unstable_cacheを使用できます。

app/blog/page.tsx
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
 
const getCachedPosts = unstable_cache(
  async () => {
    return await db.select().from(posts)
  },
  ['posts'],
  { revalidate: 3600, tags: ['posts'] }
)
 
export default async function Page() {
  const posts = getCachedPosts()
  // ...
}

その後、Server ActionsまたはRoute HandlerrevalidateTagを使用できます。

app/actions.ts
'use server'
 
import { revalidateTag } from 'next/cache'
 
export async function createPost() {
  // Invalidate all data tagged with 'posts' in the cache
  revalidateTag('posts')
}

未処理の例外の処理

データの再検証中にエラーがスローされた場合、最後に正常に生成されたデータがキャッシュから引き続き提供されます。次のリクエストで、Next.jsはデータの再検証を再試行します。エラーハンドリングの詳細はこちら

キャッシュ場所のカスタマイズ

ページのキャッシュと再検証(Incremental Static Regenerationを使用)は、同じ共有キャッシュを使用します。Vercelにデプロイする際、ISRキャッシュは耐久性のあるストレージに自動的に永続化されます。

セルフホスティングの場合、ISRキャッシュはNext.jsサーバーのファイルシステム(ディスク上)に保存されます。これは、Pages RouterとApp Routerの両方を使用してセルフホスティングする場合に自動的に機能します。

キャッシュされたページやデータを耐久性のあるストレージに永続化したり、Next.jsアプリケーションの複数のコンテナやインスタンス間でキャッシュを共有したりする場合は、Next.jsキャッシュの場所を設定できます。詳細はこちら

トラブルシューティング

ローカル開発でのキャッシュデータのデバッグ

fetch APIを使用している場合、どのリクエストがキャッシュされているか、またはキャッシュされていないかを理解するために、追加のログ記録を追加できます。loggingオプションの詳細はこちら

next.config.js
module.exports = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
}

正しい本番環境の動作の検証

ページが本番環境で正しくキャッシュされ、再検証されることを確認するには、next buildを実行してからnext startを実行して本番Next.jsサーバーを起動することでローカルでテストできます。

これにより、本番環境で動作するのと同様にISRの動作をテストできます。さらにデバッグするには、次の環境変数を.envファイルに追加してください。

.env
NEXT_PRIVATE_DEBUG_CACHE=1

これにより、Next.jsサーバーはISRキャッシュのヒットとミスのコンソールログを出力します。出力内容を検査して、next build中にどのページが生成され、オンデマンドでパスにアクセスされる際にページがどのように更新されるかを確認できます。

注意事項

  • ISRはNode.jsランタイム(デフォルト)を使用している場合にのみサポートされます。
  • 静的エクスポートを作成する場合、ISRはサポートされません。
  • 静的にレンダリングされたルート内に複数のfetchリクエストがあり、それぞれ異なるrevalidate頻度を持つ場合、ISRには最短の時間が使用されます。ただし、それらの再検証頻度はデータキャッシュによって尊重されます。
  • ルートで使用されるfetchリクエストのいずれかがrevalidate時間が0であるか、明示的なno-storeを持つ場合、そのルートは動的にレンダリングされます
  • オンデマンドISRリクエストではミドルウェアは実行されません。つまり、ミドルウェア内のパス書き換えやロジックは適用されません。正確なパスを再検証していることを確認してください。たとえば、書き換えられた/post-1ではなく、/post/1を使用します。

バージョン履歴

バージョン変更点
v14.1.0カスタムcacheHandlerが安定しました。
v13.0.0App Routerが導入されました。
v12.2.0Pages Router: オンデマンドISRが安定しました。
v12.0.0Pages Router: ボット認識ISRフォールバックが追加されました。
v9.5.0Pages Router: 安定版ISRが導入されました