{
  "$type": "site.standard.document",
  "description": "You may have loved the shared 'payload' feature when generating Nuxt 2 apps. But did you know you can do the same in Nuxt 3?",
  "path": "/blog/shared-data-nuxt-generate",
  "publishedAt": "2023-12-30T22:00:00.000Z",
  "site": "at://did:plc:jbeaa5kdaladzwq3r7f5xgwe/site.standard.publication/3gtfwwbnks225",
  "tags": [
    "nuxt",
    "generate"
  ],
  "textContent": "You may have loved the shared 'payload' feature when generating Nuxt 2 apps. But did you know you can do the same in Nuxt 3?\n\n\nWHY USE A SHARED 'PAYLOAD'?\n\nFirst, what is a shared payload - and why would you want it?\n\nRather than running the same code over and over again (for example, for every page of a blog that fetches some data from a CMS), you can perform the step once and share the data across all the generated pages.\n\nFor example, if they are used on more than one page, you might want to extract and share data like a list of breadcrumbs, or a list of blog posts, or even the social links in the footer of your site.\n\nAs the time taken to generate static pages slows down every deploy, if you share this data across successive page prerendering, it can significantly speed up the deployment process and decrease the number of hits on your content management system.\n\n\nUSING A SHARED PAYLOAD IN NUXT 2\n\nIn Nuxt 2, shared payload support was built in. You could use a shared payload as simply as this (see Nuxt 2 documentation [https://v2.nuxt.com/docs/configuration-glossary/configuration-generate/#speeding-up-dynamic-route-generation-with-payload]):\n\nimport axios from 'axios'\n\nexport default {\n  generate: {\n    routes() {\n      return axios.get('https://my-api/users').then(res => {\n        return res.data.map(user => {\n          return {\n            route: '/users/' + user.id,\n            payload: user\n          }\n        })\n      })\n    }\n  }\n}\n\n\nThis 'payload' then becomes part of the Nuxt context, accessible (for example) in your data fetching hooks:\n\nexport default {\n  async asyncData ({ params, payload }) {\n    if (payload) return { user: payload }\n    else return { user: await fetchUser(params.id) }\n  }\n}\n\n\n\nUSING A SHARED PAYLOAD IN NUXT 3\n\nWhen we released Nuxt 3, we didn't document a successor to this shared payload functionality in Nuxt 2.\n\nšŸ‘‰ Note that we also use the word 'payload' to refer to everything we transfer from the server to the client in Nuxt 3, so I've tried to use 'shared payload' consistently here - but be aware that if you're reading old articles it might be confusing.\n\nWe didn't directly port this feature across to Nuxt 3 for two reasons:\n\n 1. We want the nuxt.config to be fully serialisable. Ideally it shouldn't contain much (if any) code that couldn't fit in a normal JSON object. (And zero runtime code.) That has already unlocked future capabilities for us including the ability to have an entirely self-contained .output/ folder for Nuxt builds.\n\n 2. More significantly, we already support a much more powerful storage layer. It's possible with a very few steps to duplicate this functionality in a much more granular and customised way, with the shared storage layer we've built into Nitro [https://nitro.unjs.io/guide/storage].\n\nBefore I share an example of how you might do that yourself, I should say that there's a module to make this much easier: nuxt-prepare [https://nuxt-prepare.byjohann.dev] by Johann Schopplich. Check it out!\n\nBut if you wanted to build this yourself, here's how you might do it. This approach uses Nitro's storage layer to store data between prerendered routes, running in a Nitro server plugin:\n\nconst getPayload = dedupe(cache('payload', async () => ({\n  todos: await $fetch('https://jsonplaceholder.typicode.com/todos'),\n})))\n\nexport default defineNitroPlugin((nitroApp) => {\n  // expose payload to each request if we are prerendering\n  nitroApp.hooks.hook('request', async (event) => {\n    event.context.payload = import.meta.prerender ? await getPayload() : {}\n  })\n})\n\n// avoid duplicating calls in parallel as prerender process\n// calls a number of renders at the same time, before the\n//  first payload is initialised\nfunction dedupe<T>(fn: () => Promise<T>) {\n  let promise: Promise<T>\n  return () => {\n    return (promise ||= fn())\n  }\n}\n\n// cache result in memory by default - though\n// we could also cache it in data store:\n// šŸ‘‰ https://github.com/unjs/nitro/pull/1352\nfunction cache<T>(key: string, fn: () => Promise<T>) {\n  const data = useStorage()\n  return async () => {\n    let value = await data.getItem<any>(key)\n    if (!value) {\n      value = await fn()\n      await data.setItem(key, value)\n    }\n    return value\n  }\n}\n\n\nYou could then use this shared payload in a Vue component (or Nuxt plugin) like this:\n\nconst event = useRequestEvent()\nconst { data } = await useAsyncData(async () => {\n  if (import.meta.prerender) {\n    return event.context.payload.todos\n  }\n  return await $fetch('https://jsonplaceholder.typicode.com/todos')\n})\n\n\nThis approach uses import.meta.prerender, which allows us to tree-shake code out of our builds so you only include this payload code in your app when you are prerendering your app. (In the example above it's quite minimal, but it's worth being aware of in your own projects to optimise your bundle size.)\n\nYou can check out this full StackBlitz example [https://stackblitz.com/edit/nuxt-shared-payload?file=server/plugins/payload.ts] - make sure to run pnpm generate to confirm that the console log only runs when the first page is prerendered.\n\n\nPAYLOAD EXTRACTION\n\nIt gets even better. Nuxt turns on something called 'payload extraction' when you prerender your pages. (You can also turn it on manually even when you have a runtime server, with the experimental.payloadExtraction option.)\n\nWithout payload extraction, your data fetching composables (useFetch and useAsyncData) will rerun on client-side navigation. But with it, this data is extracted into a payload.json file which can be used instead of rerunning the composables.\n\nIf you are prerendering every route of your app, you can therefore confidently early return or throw an error which means your data fetching code can also be tree shaken out from your final app. (That's what I do in my website: see here [https://github.com/danielroe/roe.dev/blob/main/src/components/TheTalks.server.vue#L37-L38] for example.)\n\n\nNEXT STEPS FOR PRERENDERING OPTIMISATIONS\n\nšŸ‘‰ In addition to nuxt-prepare, we're already talking about how to make this easier for users (and automatic!). Follow pull request #24894 [https://github.com/nuxt/nuxt/pull/24894] for our thoughts and implementation.",
  "title": "Using shared data when generating pages",
  "updatedAt": "2026-05-18T10:17:58.927Z"
}