Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

A prerendered Nuxt page is HTML generated at build time, but it is still a Vue application when it loads in the browser. Hydration runs your component code again on the client, so any data call written directly in component setup can run a second time there. The CMS request you see in the browser is usually that call, not a sign that prerendering failed.

What prerendering does and does not change

Prerendering produces static output for the routes you select. The server renders each page once during the build, writes the HTML to disk, and serves that file on request. It does not stop the browser from running your JavaScript afterward. When the page loads, Nuxt hydrates the HTML, which means Vue attaches to the existing markup and executes the component logic again. Any network request that logic makes is a new request from the browser.

So the question is not whether the page was prerendered. The question is whether the data it needs was fetched in a way Nuxt can transfer from the server to the client. If it was not, the client has to ask for it again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why a direct $fetch call repeats the request

Nuxt’s documentation describes this exact case. Calling $fetch directly in the setup function of a component is a common source of duplicate requests. The official wording is:

“If the $fetch function is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated).”

The reason is that a plain $fetch result is not stored in the Nuxt payload. The server has the data and renders it into the HTML, but the client has no record of that response, so the setup code requests it again during hydration. Prerendering does not change this, because the build-time render goes through the same server path.

How useFetch and useAsyncData avoid the second request

For data that should be fetched once on the server and reused on the client, Nuxt’s supported pattern is useFetch or useAsyncData. These composables serialize the result into the payload. When the client hydrates and the data is present, Nuxt reads it from the payload rather than sending the request again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Several options change this behavior, and they are the first thing to check:

Setting or pattern Where the data is fetched Stored in payload Browser request on hydration
Direct $fetch in component setup Server render, then again in the browser No Yes, a repeat of the server request
useFetch or useAsyncData (defaults) Server during SSR or prerender Yes No, if the payload has the data
useAsyncData with server: false Browser only, after hydration Not applicable Yes, by design; the fetch waits until hydration
useAsyncData with serialize: false Server during SSR or prerender No Yes, if the data is rendered on the client

The server: false row is the one most often misread. It is not a bug when the request appears after hydration, because you asked for that behavior. The serialize: false row is the one most often overlooked. The server still fetches the data, but it is kept out of the payload, so the client fetches it again if the component uses it.

Check whether the request is a CMS call at all

Before changing code, confirm what the browser is requesting. A request to a URL ending in _payload.json is Nuxt loading its own payload, not a CMS API call. Depending on the payload extraction option in your Nuxt configuration, the initial page may embed the payload, or client-side navigation may fetch an extracted _payload.json file. Neither case is a CMS request, and neither means your data is being refetched from the CMS.

A step-by-step diagnosis

  1. Open the affected URL in a fresh tab with the browser developer tools open on the Network panel. Enable Preserve log, then reload the page.
  2. Find the CMS request. Note whether it is listed during the initial document load, after the page has finished hydrating, or only when you navigate to the page from another route inside the app.
  3. Open the Initiator column for that request. If it points to a component or composable file, you have found the call site. If it points to Nuxt’s internal files, check the timing against the hydration step.
  4. Open Nuxt DevTools and go to the Payload tab for the same route. Look for the CMS result in the payload data.
  5. Choose the branch that matches what you found:
    • The payload is missing the CMS data. Look for direct $fetch in setup, server: false, serialize: false, or a fetch inside client-only code such as a <ClientOnly> block or a process.client check.
    • The payload contains the CMS data, but a CMS request still appears. Assume prerendering worked. Search for a second call in a plugin, a watcher, a refresh action, or a client navigation handler. Decide whether that call is intentional freshness behavior or an unnecessary repeat of the initial fetch.
    • The request appears only on client-side navigation. This is expected for pages that were not fetched during the initial render, or for a route that depends on changing parameters. Check that the key and the fetch options match your intent.

The steps above follow from Nuxt’s documented fetch behavior. The exact cause in a given project depends on its code and request trace, which only you can check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dynamic routes and shared keys

Shared data across routes needs a key that identifies the content. If two pages share a key that does not include the slug or route parameter, the payload can hand one page’s data to another during prerendering. Nuxt’s upgrade guidance warns about this case specifically. Include the dynamic part in the key:

const { data } = await useAsyncData(`article-${route.params.slug}`, () => fetchArticle(route.params.slug))

Check this whenever a prerendered article shows another article’s content, or when a request repeats for each slug.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choosing a fix

When you change the code, compare the options on four axes:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Must the data be in the HTML on the first response? If yes, fetch on the server and serialize the result. If no, server: false is a valid choice.
  • Should the request happen once or refresh reactively? A one-time payload read suits static content. A reactive fetch suits data that changes with user input.
  • Is the content specific to a route? Give it a key that identifies the route or slug.
  • Is the content public or does it need credentials? Credentials that depend on the request may need server-side handling so they are not exposed to the browser.

The Nuxt documentation supports comparing these behaviors, but it does not prescribe a CMS-specific delivery pattern. The right choice depends on your site’s requirements.

Version notes

The behavior described here applies across Nuxt versions, but option names, defaults, and the payload extraction setting can differ. Nuxt’s documentation states that Nuxt 3 reached end of life on 31 July 2026 and no longer receives bug fixes or security patches. If you are on Nuxt 3, plan your upgrade before you change fetch code, and check the versioned documentation for your release. The Nuxt 4 prerendering page in the current documentation identifies version 4.5.2.

If you do not know your version, run npx nuxi --version in the project directory, or check the nuxt entry in package.json.

Nuxt’s official documentation for prerendering, useFetch, and useAsyncData is the authoritative reference for these options. Confirm option names there before you apply the changes in your project.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.