Nuxt 4 hasn’t yet been fully released, but you can already opt into it on Netlify. Nuxt 4 on Netlify comes with out-of-the-box support for ISR, on-demand revalidation, and all other Netlify advanced caching primitives.
You can deliver high-performance sites while keeping your content up-to-date by using Nuxt with Netlify’s advanced cache control features. In this guide, we’ll explain how and why to use different caching options, and show the code you’ll need to get fine-grained control over caching behaviors in a Nuxt site opted into version 4 mode.
#What’s new in Nuxt 4?
Check out our Nuxt 4 announcement post for details on what’s new.
The major highlight for Netlify sites is that the full Netlify caching platform is unlocked. We’ll dig into how to make the most of it below.
#How to opt into Nuxt 4 early
To opt into Nuxt 4 before it’s fully released, follow the opt-in guide. In short, just set this:
To opt into all the Netlify improvements described in this guide, you also need to set your Nuxt compatibility date to 2024-05-07 or later (we recommend setting this to the current day):
#How to upgrade to Nuxt 4
Most apps will be able to upgrade seamlessly, but be sure to follow the official 3 to 4 migration guide.
Then, deploy your site to Netlify as usual. Netlify automatically detects, builds, and deploys Nuxt sites with zero configuration. If you’ve manually configured your Nuxt 3 site, that configuration will work with Nuxt 4 as well.
#How to deploy a new Nuxt 4 site to Netlify
To deploy a new Nuxt site to Netlify or move an existing site to Netlify, you can link your repository to automatically build and deploy your app (recommended), or you can deploy manually from the command line.
Netlify CLI
Commands starting with netlify <...>
use the powerful Netlify CLI. Install it by running:
To link your site and deploy from the command line, first link your site:
You can then deploy your site by running:
Alternatively, you can link your repository in the Netlify UI to set up continuous deployment.
Try it
#What is ISR?
Incremental Static Regeneration or ISR is an approach to rendering that lets you skip rendering a given page at build time, and instead defer rendering it to the very first request, then always serve responses from the CDN edge cache and control when that page is regenerated in the background.
ISR was popularized by Next.js, which implements it internally within the framework, but it’s a pattern that can be implemented with any framework in a standards-based way using HTTP headers and a CDN that supports them.
With ISR, you can set a page to be revalidated as soon as a request is fulfilled with a stale cache
entry (based on an etag
or a time-based header like max-age
). This revalidation occurs in the
background without affecting your users.
In the case of Netlify, the CDN node will forward the request to the origin (a serverless function which renders the page with your site’s framework), and the response is cached on the CDN node and served on future requests. Clever CDNs will even deduplicate in-flight background revalidation requests for the same page.
#Why use ISR
These days, meta-frameworks like Nuxt support multiple rendering modes and even let you configure separate modes per route (Nuxt calls this hybrid rendering).
In any case, for a given route, you’re often faced with a choice between the following rendering options:
- Prerendering static HTML at build time, resulting in super fast load times from the CDN node closest to your users but requiring a full rebuild to update any content on your site.
- Server-side rendering (SSR) at request time, which can be slower but always serves the latest
content, or uses time-based caching (i.e.
max-age
) to avoid re-rendering on every request.
Luckily you can have the best of both worlds if you use an advanced CDN: pages that are delivered quickly at runtime with fresh content from the edge cache, without triggering builds, not even incremental builds.
#How to implement ISR using Nuxt
A modern CDN like Netlify’s edge network gives you powerful tools to control your site’s cache using just HTTP headers. Nuxt lets you set rules on a page-by-page basis, so you can control how each page is cached and when it is regenerated.
When using universal rendering with Nuxt you can opt into ISR via routeRules
:
The isr
option tells the CDN to trigger a background revalidation for a route once a request comes
in for that route after its cache has become stale (5 minutes after the last render, in the example
above).
Other than on the very first request, the page load is never blocked for the user, though it won’t be perfectly fresh for everyone. Of course, you control exactly how much “staleness” is acceptable for each page. If the data is unlikely to change, you can set a long expiration time. Don’t worry if you find you need to change it later – any new deploy will clear the CDN cache instantly and your visitors will get the fresh content, without broken asset links.
If you’re just using this pattern to defer rendering static pages to save on build time, you can instruct the Netlify CDN to cache them indefinitely after the first request (don’t worry - the cache will be immediately invalidated on a new deploy or rollback!):
#How does ISR work?
Under the hood, responses are just separate cache headers for the Netlify CDN
(Netlify-CDN-Cache-Control
) and the browser (Cache-Control
):
Thanks to conditional requests, only content that has actually changed is downloaded by the browser.
The durable
directive tells the
CDN to also store responses in a secondary cache that is globally accessible to all edge nodes.
If you’ve opted in to deployment on Netlify Edge Functions,
note that the durable
directive is not yet supported. In this case, each node in the Netlify CDN only maintains its
own independent cache.
This is all just syntactic sugar. If you prefer, you can set these headers manually and tweak anything to your heart’s content:
#On-demand revalidation with cache tags
Up until now we’ve just been caching for a fixed amount of time. This is fine if you don’t know when
a data source has updated, but if we can get notifications when it has changed, we can revalidate
the cache for only the page where it is needed. This is because Netlify supports the Cache-Tag
header, with fine-grained revalidation by tag. Netlify also supports the Netlify-Cache-Tag
header
with the same meaning but specific to just Netlify’s CDN, and which will not be visible to the
browser. Most CMS and ecommerce platforms support webhooks to report content changes, so you can
use this to trigger a revalidation of the cache for only the pages that need it.
To see how cache tags work, let’s look at an example. Cache tags let you mark each page with a tag that can then be used to invalidate the cache for any page with that tag. One of the most powerful uses for this is to tag a page with the IDs of the data that it uses. This means that when that data is changed, you can invalidate only the pages that use that data. This is a powerful way to do ISR, because you can keep the cache fresh for only the pages that need it, and not waste time re-rendering pages that haven’t changed. If you edit one entry in the CMS, the cache will be invalidated just for pages that use that data and not the whole site.
#How to revalidate your pages on demand with cache tags and webhooks
This example shows how to use Contentful webhooks to do on-demand revalidation, but you can use a similar approach with any CMS.
The first step is to tag each page with the data IDs. Here’s an example of how you might do this with Contentful:
This sets the cache tag to the book slug, which in this example is the Contentful object ID. When the book is updated, the cache for this page is invalidated.
Next you can use a webhook to revalidate the cache for this page when the book is updated:
This assumes you’ve populated your Contentful webhook secret as a CONTENTFUL_WEBHOOK_SECRET
environment variable in your Netlify
site and told Nuxt about it:
This uses the purgeCache
function from the @netlify/functions
package to invalidate the cache
for all pages that reference that book. You can then set up a webhook in Contentful to call this
webhook when a book is updated.
Next, create a webhook
that posts to /api/books/webhook
. Choose “Select specific triggering events” and choose “Entry”.
#Fine-grained cache control
So far we’ve been caching each page as the same for each visitor. This is probably what you want for
most public pages, but not always. The Vary
header can be used to store different versions of a
page according to the request headers. However this can be quite an inflexible instrument, so
Netlify extended this with the Netlify-Vary
header.
An important case will be to control how query parameters are treated. The strict spec-compliant way
that a cache will handle these is to treat the whole URL complete with query as the cache key,
meaning that ?foo=bar
and ?foo=baz
will be treated as different pages. While this is what you
want in some cases, there are a lot of times when this will break caching entirely. A very common
example for this is social sharing. Links from social media will often have tracking parameters
added automatically, and you don’t want to cache a different copy of the page for each visitor. This
is also the case if you are using ads with tracking IDs. Should ?fbclid=nnnn...
be stored as a
separate page than ?fbclid=mmmm...
? Probably not. However you might be using the query param for
something that you do want to use for the cache.
A typical example might be something like sorting, filtering, or pagination. Netlify lets you control
exactly which query params are used for the cache key using the Netlify-Vary
header. Here’s how
you can use it on a listings page:
In this example it will treat pages with different values for sort
, page
and per_page
as
different pages, but will ignore any other query params. This is a powerful tool for controlling
your cache, and can be used in combination with cache tags for even finer control.
#Cookies and caching
If there are user preferences that change how a page is displayed they need not break caching. You
probably shouldn’t vary on the whole Cookie header, because this will mean that every user need
their own version of the page cached, but you can use the Netlify-Vary
header to control exactly
which cookies are used for the cache key. Here’s an example using a cookie to control the theme:
You can use this to choose cookies with low cardinality (i.e. ones that have a small number of possible values) to vary the cache on, which means you can have customized pages while still caching them. If there is a light theme and a dark theme, each of these will then be cached separately. You shouldn’t use it for cookies that have high cardinality, like session or user IDs.
#Debugging your cache
Advanced cache tools like this can be tough to debug. The Cache-Status
header can help, by showing whether the CDN had a had a
hit, miss or stale response. Netlify supports this header, and you can use it to see what’s
happening with your cache.
No caching in development
When running netlify dev
it will strip all CDN cache headers, but the cache itself is not used. You need to deploy
your site to see the cache in action.
Nuxt gives you powerful tools to control your cache when combined with a platform like Netlify that supports advanced caching features. This gives you the power to control your cache in a way that’s standards-based and uses the web platform.
#Get started
To create your own Nuxt site to deploy to Netlify, you can get started by running npx nuxi init <project-name>
. When you’re ready, link your repository to
Netlify to deploy automatically.
Or if you prefer, you can also deploy an example directly to Netlify from here by clicking the button below.
For more details see the Nuxt on Netlify docs.