<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mainuddin Mehedi</title>
    <description>The latest articles on DEV Community by Mainuddin Mehedi (@mainuddin_mehedi_ca0c9234).</description>
    <link>https://dev.to/mainuddin_mehedi_ca0c9234</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1857121%2F5d09825a-337b-4113-90e7-1605ec2b0d5f.png</url>
      <title>DEV Community: Mainuddin Mehedi</title>
      <link>https://dev.to/mainuddin_mehedi_ca0c9234</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mainuddin_mehedi_ca0c9234"/>
    <language>en</language>
    <item>
      <title>Next.js 16 Cache Components: The End of Route-Level Caching Chaos (with use cache &amp; PPR)</title>
      <dc:creator>Mainuddin Mehedi</dc:creator>
      <pubDate>Tue, 31 Mar 2026 15:44:28 +0000</pubDate>
      <link>https://dev.to/mainuddin_mehedi_ca0c9234/nextjs-16-cache-components-the-end-of-route-level-caching-chaos-with-use-cache-ppr-2ekm</link>
      <guid>https://dev.to/mainuddin_mehedi_ca0c9234/nextjs-16-cache-components-the-end-of-route-level-caching-chaos-with-use-cache-ppr-2ekm</guid>
      <description>&lt;p&gt;In the recent Next.js 16 update, the team introduced a new thing called &lt;strong&gt;cache components&lt;/strong&gt;. It's a new model with a fundamental shift in how we think about caching and rendering in Next.js applications. Leveraging &lt;strong&gt;Partial Pre-Rendering (PPR)&lt;/strong&gt; and &lt;code&gt;use cache&lt;/code&gt; directive to enable instant navigation with granular control over what gets cached and when.&lt;/p&gt;

&lt;p&gt;Unlike the previous model where caching used to be implicit, heuristic-based, and largely route-bound, the new model makes caching &lt;strong&gt;explicit and component-centric&lt;/strong&gt;. This new method allows us to have granular control over the whole caching model of our application by allowing us to explicitly cache at component, function and even at the file level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: The New Caching Pattern at a Glance
&lt;/h2&gt;

&lt;p&gt;Enable &lt;code&gt;cacheComponents&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cacheComponents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core idea&lt;/strong&gt;: Everything is &lt;strong&gt;dynamic by default&lt;/strong&gt;. You &lt;strong&gt;explicitly opt in&lt;/strong&gt; to caching at the component or function level using the &lt;code&gt;'use cache'&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key APIs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'use cache'&lt;/code&gt; → Caches the return value (data or rendered UI)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cacheLife('hours')&lt;/code&gt; (or custom profile) → Controls time-based revalidation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cacheTag('products')&lt;/code&gt; + &lt;code&gt;updateTag('products')&lt;/code&gt; → On-demand invalidation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;revalidateTag&lt;/code&gt; → Stale-while-revalidate behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How rendering and caching now blend (PPR)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static shell (pages prerendered at build time)&lt;/li&gt;
&lt;li&gt;Cached dynamic components (rendered at build, cached, revalidated automatically)&lt;/li&gt;
&lt;li&gt;True dynamic holes (wrapped in &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;, streamed at request time)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates &lt;strong&gt;instant static shells&lt;/strong&gt; with fresh or cached dynamic content giving us the best of both worlds.&lt;/p&gt;

&lt;p&gt;Now let's look into the Next.js caching system and establish a contrasting view of both approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previous Model
&lt;/h2&gt;

&lt;p&gt;So, the thing that has changed in Next.js caching is the handling of &lt;strong&gt;Full route cache&lt;/strong&gt; and &lt;strong&gt;Data cache&lt;/strong&gt;. The other two caching layers remain the same and are very simple.&lt;/p&gt;

&lt;p&gt;Before getting into it let's have a look at the four layers of caching in Next.js and how they worked according to the previous caching system.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;How it worked&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Request Memoization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deduplicates identical &lt;code&gt;fetch&lt;/code&gt; calls during a single render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Empty unless you explicitly cached (&lt;code&gt;fetch&lt;/code&gt; with &lt;code&gt;force-cache&lt;/code&gt; or &lt;code&gt;unstable_cache&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full Route Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Implicitly cached static routes (RSC payload/HTML) in the server. Opted out when dynamic APIs detected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Router Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client-side RSC payload cache for soft navigation. Pages are cached in the client as the user navigates.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Full Route Cache
&lt;/h3&gt;

&lt;p&gt;In the previous model, Full route cache used to be handled implicitly for a purely static page. This means pages that don't have any dynamic data are automatically cached. But the problem was that we didn't have any option to handle things from the component level. For this reason, whenever a page had dynamic operations that are uncached, the whole route became dynamic. It didn't matter whether I had a purely static component. Just because it was used in a dynamic page, it re-rendered despite not having any dynamic data and no changes.&lt;/p&gt;

&lt;p&gt;Though we had our option to work with route level caching using methods like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.js — Route-level configuration only&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// 'force-dynamic' | 'force-static' | 'error'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Complex override options&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem was that one uncached &lt;code&gt;fetch&lt;/code&gt; in a deeply nested component would force the &lt;strong&gt;entire route&lt;/strong&gt; to be dynamic. You couldn't easily have a mostly-static page with one dynamic widget without complex workarounds or splitting into multiple routes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetchCache&lt;/code&gt; option in the route level configuration provided many options to handle how &lt;code&gt;fetch&lt;/code&gt; requests are cached but they were confusing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Complex override options -&lt;/span&gt;
&lt;span class="c1"&gt;// 'auto' | 'force-cache' | 'force-no-store' | 'only-cache' | 'only-no-store'&lt;/span&gt;
&lt;span class="c1"&gt;// 'default-cache' | 'default-no-store'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It threw error if the route level configuration didn't match with the individual fetch configurations for cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Cache
&lt;/h3&gt;

&lt;p&gt;The control we had over the caching system was at the &lt;strong&gt;Data Cache&lt;/strong&gt; level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before Next.js 15 (Oct 2024):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fetch&lt;/code&gt; requests were cached indefinitely by default (&lt;code&gt;cache: 'force-cache'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;This led to stale data issues, requiring explicit &lt;code&gt;revalidate&lt;/code&gt; or &lt;code&gt;cache: 'no-store'&lt;/code&gt; to opt out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The "Auto" Heuristic:&lt;/strong&gt;&lt;br&gt;
With &lt;code&gt;dynamic: 'auto'&lt;/code&gt; (the default), Next.js applied a complex rule: it cached &lt;code&gt;fetch&lt;/code&gt; requests discovered &lt;strong&gt;before&lt;/strong&gt; any dynamic APIs (&lt;code&gt;cookies()&lt;/code&gt;, &lt;code&gt;headers()&lt;/code&gt;, etc.) were used, but not those discovered &lt;strong&gt;after&lt;/strong&gt;. This execution-order dependency confused many developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next.js 15+:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fetch&lt;/code&gt; requests are &lt;strong&gt;NOT cached by default&lt;/strong&gt; (&lt;code&gt;cache: 'no-store'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You must explicitly opt in with &lt;code&gt;cache: 'force-cache'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Caching &lt;code&gt;fetch&lt;/code&gt; requests
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;force-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Opt-in to caching&lt;/span&gt;
  &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Revalidate every hour&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// For on-demand invalidation&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;unstable_cache&lt;/code&gt; for Non-Fetch Data
&lt;/h4&gt;

&lt;p&gt;For database queries or other async operations that weren't native &lt;code&gt;fetch&lt;/code&gt; calls, was cached using &lt;code&gt;unstable_cache&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;unstable_cache&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCachedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unstable_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-cache-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Cache key segments&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Seconds&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;// For revalidation&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCachedUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wrapped arbitrary async functions in the Data Cache. But it required manually constructing cache keys and managing revalidation tags across the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React &lt;code&gt;cache()&lt;/code&gt; (Request Memoization only)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React's &lt;code&gt;cache()&lt;/code&gt; provided deduplication &lt;strong&gt;within a single request's render tree&lt;/strong&gt;, preventing duplicate database hits if the same function was called multiple times during one render. &lt;strong&gt;It didn't persist across users, builds, or page navigations&lt;/strong&gt;. Once the request ended, the cache was gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Revalidation in the Previous Model
&lt;/h3&gt;

&lt;p&gt;Revalidation was handled in two ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time-based:&lt;/strong&gt; Set &lt;code&gt;next.revalidate&lt;/code&gt; on &lt;code&gt;fetch&lt;/code&gt; or &lt;code&gt;revalidate&lt;/code&gt; in &lt;code&gt;unstable_cache&lt;/code&gt; config to automatically refresh after a set duration. We could also revalidate in the route level configuration using &lt;code&gt;export const revalidate = 60&lt;/code&gt; for time-based revalidation of the entire route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-demand:&lt;/strong&gt; Use &lt;code&gt;revalidateTag('tag-name')&lt;/code&gt; or &lt;code&gt;revalidatePath('/path')&lt;/code&gt; inside a Server Action or Route Handler to immediately purge cached data after mutations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pain Points
&lt;/h3&gt;

&lt;p&gt;The previous model created several developer experience issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Route-level granularity:&lt;/strong&gt; You couldn't mix static and dynamic content on the same page easily. One dynamic component (needing cookies) forced the entire route to render dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implicit behavior:&lt;/strong&gt; The "auto" heuristics were unpredictable. Whether a fetch was cached depended on execution order relative to dynamic APIs. (Fixed with Next.js 15 update).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration overload:&lt;/strong&gt; You needed to understand &lt;code&gt;dynamic&lt;/code&gt;, &lt;code&gt;fetchCache&lt;/code&gt;, &lt;code&gt;revalidate&lt;/code&gt;, &lt;code&gt;unstable_cache&lt;/code&gt;, and React's &lt;code&gt;cache()&lt;/code&gt; each with complex option sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No component boundaries:&lt;/strong&gt; To cache a component, you had to cache the entire route or use experimental PPR with confusing segment configs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  New Model: Explicit, Component Level Caching
&lt;/h2&gt;

&lt;p&gt;Next.js 16's Cache Components solve these problems by inverting the model: &lt;strong&gt;nothing is cached unless you explicitly say so&lt;/strong&gt;, and you can now mark individual components, functions, or even UI segments for caching while leaving the rest dynamic.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mental Shift
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Previous Model&lt;/th&gt;
&lt;th&gt;New Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Default behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heuristic-based (cached before dynamic APIs)&lt;/td&gt;
&lt;td&gt;Nothing cached by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Granularity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Route-level only (page/layout)&lt;/td&gt;
&lt;td&gt;Component, function, or file level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Opt-in method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cache: 'force-cache'&lt;/code&gt; on fetch, route segment configs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;'use cache'&lt;/code&gt; directive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Non-fetch caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;unstable_cache&lt;/code&gt; or React &lt;code&gt;cache()&lt;/code&gt; (limited)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;'use cache'&lt;/code&gt; wraps any function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static/Dynamic mix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Required complex PPR flags&lt;/td&gt;
&lt;td&gt;Native via Suspense boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Core Philosophy
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic by default&lt;/strong&gt; - nothing is cached unless you say so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit at component/function level&lt;/strong&gt; - caching is declared where the work happens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified API&lt;/strong&gt; for data &lt;strong&gt;and&lt;/strong&gt; UI caching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial Prerendering (PPR) is the default&lt;/strong&gt; - static shell + dynamic streaming.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is pretty clear. Instead of reasoning about route-level heuristics and hoping the framework guesses right, you now just tell Next.js what to cache right where the code lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  How &lt;code&gt;use cache&lt;/code&gt; Works
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;'use cache'&lt;/code&gt; directive is the foundation. When added above a component or function, it tells Next.js to cache the result:&lt;/p&gt;

&lt;h4&gt;
  
  
  File Level - Cache everything exported
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// At the very top of the file&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* render */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// All exports in this file are cached&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Component-level caching
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM products WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Function-level caching
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cacheTag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nf"&gt;cacheTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`product-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM products WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually that's all there is to it. We don't need to construct cache keys, no wrapper functions, no route configs. It's just a directive that is placed right where the caching is taking place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Lifecycle Control
&lt;/h3&gt;

&lt;p&gt;Cache lifecycle is encouraged to be controlled via tagging. When using the &lt;code&gt;use cache&lt;/code&gt; directive, you can set a tag for the cache using &lt;code&gt;cacheTag&lt;/code&gt;. Then later on you can use that tag with &lt;code&gt;updateTag&lt;/code&gt; or &lt;code&gt;revalidateTag&lt;/code&gt; to invalidate the cache. &lt;br&gt;
You can also use &lt;code&gt;revalidatePath&lt;/code&gt; to invalidate all cached data for a specific route path. &lt;/p&gt;
&lt;h4&gt;
  
  
  Golden Rule:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;updateTag()&lt;/code&gt; can &lt;strong&gt;only&lt;/strong&gt; be used in &lt;strong&gt;Server Actions&lt;/strong&gt; (&lt;code&gt;'use server'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;revalidateTag&lt;/code&gt; in a Server Action or Route Handler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time-based revalidation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cacheLife&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;WeatherWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nf"&gt;cacheLife&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Uses predefined profile&lt;/span&gt;
  &lt;span class="c1"&gt;// Or custom: cacheLife({ stale: 3600, revalidate: 900, expire: 86400 })&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchWeather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;°&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On-demand invalidation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In your component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cacheTag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nf"&gt;cacheTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In a Server Action&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;updateTag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;updateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Immediately invalidate&lt;/span&gt;
  &lt;span class="c1"&gt;// or you may do &lt;/span&gt;
  &lt;span class="nf"&gt;revalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;max&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// stale-while-revalidate&lt;/span&gt;
  &lt;span class="c1"&gt;// Stale content is served immediately while fresh content loads in the background.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: &lt;br&gt;
 The second argument in &lt;code&gt;revalidateTag&lt;/code&gt; sets how long stale content can be served while fresh content generates in the background. Using&amp;nbsp;&lt;code&gt;'max'&lt;/code&gt;&amp;nbsp;gives the longest stale window.&lt;/p&gt;
&lt;h3&gt;
  
  
  Partial Pre-Rendering (PPR) Integration
&lt;/h3&gt;

&lt;p&gt;The new model shines with the combination of PPR. You can now compose static and dynamic content naturally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Static: Cached at build time */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Static: Cached with 1-hour revalidation */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductRecommendations&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Dynamic: Streamed at request time */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CartSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ShoppingCart&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Uses cookies, not cached */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Dynamic: User-specific, not cached */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProfileSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// components/ProductRecommendations.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cacheLife&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductRecommendations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nf"&gt;cacheLife&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM recommendations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductGrid&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the page naturally composes all three - a static header, cached recommendations, and dynamic user content without a single route segment config. This is the composability that was missing before.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Gets Cached?
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;'use cache'&lt;/code&gt;, Next.js caches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The return value (data or rendered UI)&lt;/li&gt;
&lt;li&gt;The component's rendered output (if used in a component)&lt;/li&gt;
&lt;li&gt;Arguments passed to the function (as cache key parts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database queries (Prisma, Drizzle, raw SQL)&lt;/li&gt;
&lt;li&gt;External API calls (even non-&lt;code&gt;fetch&lt;/code&gt; ones)&lt;/li&gt;
&lt;li&gt;Expensive computations&lt;/li&gt;
&lt;li&gt;Entire component render outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The shift from Next.js's previous caching model to cache components is more than just a new API, it's a fundamentally different way of thinking. Instead of fighting route-level heuristics and managing a patchwork of &lt;code&gt;fetch&lt;/code&gt; options, &lt;code&gt;unstable_cache&lt;/code&gt;, and segment configs, you now have a single directive &lt;code&gt;'use cache'&lt;/code&gt; that works at the component, function, or file level.&lt;/p&gt;

&lt;p&gt;The mental model is clean: everything is dynamic by default. You explicitly opt in to caching exactly where you need it, with &lt;code&gt;cacheLife&lt;/code&gt; for time-based control and &lt;code&gt;cacheTag&lt;/code&gt;/&lt;code&gt;updateTag&lt;/code&gt; for on-demand invalidation. Combined with PPR, this means your pages get instant static shells while streaming fresh content, no complex configuration required.&lt;/p&gt;

&lt;p&gt;If you've ever been frustrated by a single &lt;code&gt;cookies()&lt;/code&gt; call making your entire page dynamic, or puzzled over why your data was stale despite setting &lt;code&gt;revalidate&lt;/code&gt;, this new model is the answer. It puts caching decisions where they belong, next to the code that actually needs them.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>nextjs</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
