Enable Cache Components

Version:

Next.js 16 introduces Cache Components that replaces the old implicit caching model and makes data fetching dynamic by default. You have to use the 'use cache' directive to perform caching giving you more control over what is cached and their duration.

For a SitecoreAI Content SDK application, this means:

  • Editing and preview modes always bypass the cache.

  • Every component is handled individually and you should not wrap the <Placeholder/> component with <Suspense/>.

  • Cached data is automatically refreshed after a set duration using cacheLife.

  • You can manually invalidate cached data after a mutation using revalidateTag, updateTag, or revalidatePath.

  • You can render content at runtime using the connection() function and not pre-render at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as Math.random() or new Date().

To set up Cache Components in your Content SDK Pages Router app:

  1. In your next.config.ts file, enable the cacheComponents flag as shown:

    const nextConfig: NextConfig = {
      cacheComponents: true,
      ...
    };
  2. All route handlers are dynamic by default when using Cache Components. The route segment configuration in your route handlers is therefore incompatible and must be removed. In the following files:

    • src/app/api/editing/config/route.ts

    • src/app/api/editing/render/route.ts

    • src/app/api/sitemap/route.ts

    • src/app/api/robots/route.ts

    Remove the following line:

    export const dynamic = 'force-dynamic';
  3. You must create cached versions of your data fetching functions, especially for page, dictionary, and error page requests. In src/lib, create a file called cached-functions.ts and add the following:

    import { ErrorPage } from '@sitecore-content-sdk/nextjs';
    import client from './sitecore-client';
    
    export async function getPage(path: string[], { site, locale }: { site: string; locale: string }) {
      'use cache';
      return client.getPage(path, { site, locale });
    }
    
    export async function getErrorPage(
      code: ErrorPage,
      { site, locale }: { site: string; locale: string }
    ) {
      'use cache';
      return client.getErrorPage(code, { site, locale });
    }
    
    export async function getDictionary({ site, locale }: { site: string; locale: string }) {
      'use cache';
      return client.getDictionary({ site, locale });
    }
  4. After creating the cached versions of your data fetching functions, you must now update references to use them.

    • In /[[...path]]/page.ts, replace instances of the client.getPage function in the component and generateMetadata functions with the getPage function from src/lib/cached-functions.ts:

      // Before
      await client.getPage(path ?? [], { site, locale });
      
      // After
      import { getPage } from 'src/lib/cached-functions';
      
      await getPage(path ?? [], { site, locale });
      Important

      Do not wrap the component with 'use cache';.

      In the draft.isEnabled block, do not route client.getPreview and client.getDesignLibraryData through cache helpers.

    • In src/app/not-found.tsx and /[[...path]]/not-found.tsx, replace instances of client.getErrorPage with the getErrorPage function from src/lib/cached-functions.ts:

      // Before
      await client.getErrorPage(ErrorPage.NotFound, { site, locale });
      
      // After
      import { getErrorPage } from 'src/lib/cached-functions';
      
      await getErrorPage(ErrorPage.NotFound, { site, locale });
      Important

      Do not apply 'use cache'; to the entire NotFound component. The file relies on getCachedPageParams() inside the cached boundary, and React cache boundaries do not align with Next.js cache context in this case.

    • In global-error.tsx, verify that it renders a dedicated layout that does not reference any cache components, as global-error is a client component. You will most likely need to introduce a separate layout rather than using a shared one.

    • In i18n/request.ts, replace instances of client.getDictionary with the getDictionary function from src/lib/cached-functions.ts:

      // Before
      await client.getDictionary({ site, locale });
      
      // After
      import { getDictionary } from 'src/lib/cached-functions';
      
      await getDictionary({ site, locale });

If your SitecoreClient or another fetch layer also uses Next.js fetch caching options such as cache: 'force-cache', next: { revalidate }, or next: { tags }, then you effectively have two caching layers.

  • 'use cache' on helper functions like getPage and getDictionary, which caches at the application level and can be invalidated with revalidateTag using cacheTag names.

  • The fetch data cache, controlled by fetch options and invalidated through tags on fetch requests if you use them.

To keep behavior predictable, it is recommended to prefer one main caching strategy and avoiding conflicting time-to-live (TTL) assigned to the same GraphQL response. If you use both tag systems, make sure webhook invalidation covers both naming schemes.

If you have suggestions for improving this article, let us know!