Lazy‑Loading Dynamic Routes in Nuxt 3: Splitting Code and Fetching Data Efficiently
Learn how Nuxt 3’s file‑based routing automatically lazy‑loads dynamic routes and how to pair them with server API routes to keep initial JavaScript small while fetching data on demand.
16 May 2026, 03:19 UTC

Problem: Unnecessary JavaScript bloats initial load
When building a content‑heavy site with many detail pages (e.g., blog posts, product pages), loading every page’s JavaScript up front slows down the first paint. Users on slower connections wait for code they may never need.
Thesis: Nuxt 3’s file‑based routing already gives you lazy‑loaded dynamic routes; pairing them with server‑side API routes lets you keep the initial bundle small while still fetching data on demand.
How Nuxt 3 creates lazy chunks
Each .vue file under pages/ becomes a separate route. During build, Nuxt runs Vite to split the app into chunks named after the route file. Navigating to a route triggers a dynamic import of its chunk, so the browser only downloads the code it needs.
Worked example: a blog with lazy‑loaded posts and an API route
Create a fresh project (run in a terminal with
npmoryarn; no special permissions needed):npx nuxi init nuxt-blog && cd nuxt-blog npm installAdd a dynamic route for individual posts:
# file: pages/posts/_id.vue {{ post.title }}Add an API route that returns JSON (Nuxt 3 server directory):
# file: server/api/posts/[id].get.js export default defineEventHandler(async (event) => { const { id } = event.context.params // In a real app you would query a DB; here we mock a response return { id, title: `Post ${id}`, content: `This is the body of post ${id}.
` } })Start the dev server and observe lazy loading:
Opennpm run devhttp://localhost:3000/posts/42. In the browser’s Network tab, filter by JS and notice that a chunk likeposts-_id.vue.[hash].jsis fetched only after navigation to that URL. The initial page load does not contain that chunk.Verify the split in the build output (optional):
You should see a file matching the patternnpm run build ls .output/server/chunks | grep postsposts-_id.*.js, confirming that Nuxt created a separate chunk for the dynamic route.
Trade‑off: route‑level code splitting vs. granular component splitting
Lazy‑loading at the route level is simple and works well when each page has a distinct UI. However, if a dynamic route shares large components (e.g., a rich editor or a map) with other pages, those components are duplicated in each chunk. For finer control you can move shared heavy components into separate composables/ or plugins/ and import them lazily with defineAsyncComponent, but that adds complexity.
Actionable closing
Start by placing every distinct page under pages/ and let Nuxt handle the split. When you notice duplicated code across route chunks, extract the shared parts and load them asynchronously. Regularly check the .output/ directory after a build to confirm that each route still maps to its own chunk, ensuring you keep the initial payload as small as possible.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.