skip to content

Nuxt.js

Nuxt is to Vue what Next is to React: file-based pages, auto-imports, Nitro server routes and a rendering mode per route. Interviewers go here once SSR, SEO or a BFF comes up.

on this pageshow

explore

questions

page 1 of 2

In a Nuxt 4 app, what is auto-imported without an import line, and how does a function in app/composables/ get picked up?

level: juniorimportance: must knowfreq 50%

answer

  1. three scanned app directories
  2. Vue and Nuxt APIs come built in
  3. top-level files only
  4. default export takes the file name
  5. #imports and .nuxt/imports.d.ts

basics

~20 s

Nuxt 4 auto-imports Vue APIs like ref, its own composables like useState, components from app/components/, and the exports of top-level files in app/composables/ and app/utils/. The build injects real imports only where a file uses those names.

solid answer

~40 s

Nuxt 4 auto-imports three kinds of names into app code: Vue's APIs such as `ref`, `computed` and `watch`; Nuxt's own composables such as `useState`, `useRoute` and `navigateTo`; and your code from `app/composables/`, `app/utils/` and `shared/utils/`, plus components from `app/components/`. For composables, Nuxt scans only the top-level files of `app/composables/`: each named export keeps its name, and a default export is named after the file in camelCase, so `use-cart-total.ts` gives `useCartTotal`. Files in subfolders are skipped unless re-exported from `index.ts` or added through `imports.dirs`. These aren't globals: at build time Nuxt inserts real `import` statements for exactly the names a file uses, so unused code is still tree-shaken, and `#imports` exposes the same bindings explicitly.

code

ts · 7 lines
ts
// app/composables/useCounter.ts: named export -> useCounter()
export const useCounter = () => useState('counter', () => 0)

// app/utils/format-price.ts: default export -> formatPrice()
export default function (cents: number) {
  return (cents / 100).toFixed(2)
}

go deeper

for a junior

Recall the sources: Vue APIs, Nuxt composables, and the top-level files of app/composables/, app/utils/ and app/components/.

for a middle

Explain the scanning rules: named versus default exports, why nested folders are skipped, and how #imports and generated types fit in.

for a senior

Show where auto-imports stop: server code, node_modules and call sites that need the Nuxt context, and how that shapes code layout in a large app.

for a principal

Weigh convenience against explicitness for a big team: which code should stay auto-imported and which should require explicit imports.

## What arrives without an import line In a Nuxt 4 app, most names you use in `app/` code are **auto-imported**: you write `ref(0)` or `useState('counter')` and never add an `import` statement. The sources are fixed: | Source | Examples | Where usable | |---|---|---| | Vue APIs | `ref`, `computed`, `watch`, `onMounted` | app code | | Nuxt composables and utils | `useState`, `useRoute`, `navigateTo`, `useNuxtApp` | app code | | `app/composables/` | your `useCounter` | app code | | `app/utils/` | your `formatPrice` | app code | | `shared/utils/`, `shared/types/` | code shared by app and server | app and server | | `app/components/` | `<AppHeader />`, `<BaseFooButton />` | templates | | module presets | composables a module registers | app code | `app/composables/` and `app/utils/` are scanned the same way. The split between them is a semantic one: stateful composables in one, plain helpers in the other. ## How app/composables/ is scanned 1. Nuxt scans the **top-level files** of `app/composables/` and `app/utils/`. 2. Every **named export** of those files becomes an auto-import under its own name: `export const useCounter = () => ...` gives `useCounter`. 3. A **default export** is registered under the camelCase form of the file name: `use-cart-total.ts` or `useCartTotal.ts` gives `useCartTotal`. 4. Files in **subfolders are not scanned**. `app/composables/cart/useCart.ts` stays invisible until you re-export it from `app/composables/index.ts` or add the folder through `imports.dirs`. 5. The dev server picks up new top-level files, and adding or removing a scanned directory restarts it. ## A build transform, not globals Auto-imports are **not global variables**. At build time Nuxt's transform looks at each file, finds the auto-importable names it uses, and inserts real `import` statements for exactly those names. Consequences: - **Tree-shaking still works.** A composable nobody uses never enters the bundle; the docs stress that only what is used reaches production code. - **Types come from generated files.** Nuxt writes `.nuxt/imports.d.ts`. Until `nuxt prepare`, `nuxt dev` or `nuxt build` has run, the editor reports errors such as `Cannot find name 'useBar'`. - **Every auto-import has an explicit path too.** `import { ref, useState } from '#imports'` names the same bindings, which helps when reading unfamiliar code or when auto-imports are switched off. - **Files in `node_modules` get no injected imports**, so a published library must import what it uses. ## Components follow their own rules - A component's name comes from its path, with duplicate segments removed: `app/components/base/foo/Button.vue` is `<BaseFooButton />`. `pathPrefix: false` in the `components` config registers it as `<Button />` instead. - The `Lazy` prefix, as in `<LazyMountainsList />`, loads the component's code only when it renders. - For `<component :is>`, use `resolveComponent('MyButton')` with a literal string, or import the component from `#components`. ## Where auto-imports do not reach - **`server/` code** gets Nitro's own auto-imports, `server/utils/` plus `shared/`; `app/composables/` and `app/utils/` are not visible there. - **Context-bound composables** still need a valid call site. Most Nuxt composables must run in `setup`, a plugin or route middleware, whether they were imported automatically or by hand. ## Common mistakes - Expecting a composable in a nested folder to be found. - Treating auto-imports as globals that inflate every bundle. - Importing app composables into `server/` code. - Blaming Nuxt for missing types before the dev server has generated them. ## Checking what a name resolves to When a name behaves unexpectedly, look at what Nuxt generated rather than guessing: - **`.nuxt/imports.d.ts`** declares every auto-imported name together with the file or package it comes from, so a missing or surprising source is visible at a glance. - **Go to definition** in an editor follows those generated declarations to the real file. - **An explicit import** of the same name, from a file path or from `#imports`, removes all doubt in the file where it matters. This habit matters most in larger apps, where several folders, layers and modules all contribute names to the same pool.

  • How would you auto-import Pinia-style stores kept in app/stores/?
    Add the folder to `imports.dirs`, for example `imports: { dirs: ['stores'] }` in `nuxt.config.ts`. The option only adds directories; `composables/` and `utils/` stay scanned. The same scanning rules apply: top-level files, named exports by name, and default exports by camelCase file name.
  • A colleague asks whether auto-imports make the bundle bigger. What do you answer?
    No. They are a build-time transform, not globals: for each file Nuxt inserts `import` statements only for the auto-importable names that file actually uses. Unused composables never get imported, so the bundler tree-shakes them as usual. The cost is elsewhere: less explicit code and a risk of name collisions.

saying these in an interview costs you the question

  • Auto-imports are registered as global variables on window
  • Every file in any subfolder of composables/ is auto-imported
  • Only default exports from composables/ are auto-imported
  • Auto-imported composables are bundled even when unused
  • app/utils/ helpers are auto-imported into server/ handlers too
open as a page

In Nuxt 4, why should a page load its data with `useFetch` or `useAsyncData` instead of calling `$fetch` in `<script setup>`, and when is `$fetch` right?

level: juniorimportance: must knowfreq 62%

basics

~20 s

useFetch and useAsyncData fetch during server rendering and ship the result in the Nuxt payload, so hydration reuses it instead of refetching. A bare $fetch in setup runs on the server and again in the browser; keep $fetch for user-triggered calls.

open as a page

In Nuxt 4, which app/pages/ files serve /, /recipes and /recipes/lemon-tart, and how does the recipe page read its slug?

level: juniorimportance: must knowfreq 58%

basics

~10 s

In Nuxt 4, app/pages/index.vue serves /, app/pages/recipes/index.vue serves /recipes and app/pages/recipes/[slug].vue serves /recipes/lemon-tart. That page reads the segment through the auto-imported useRoute(): route.params.slug is 'lemon-tart'.

open as a page

In Nuxt 4, what HTML does a visitor first receive under the default ssr: true, under ssr: false, and from nuxt generate?

level: juniorimportance: must knowfreq 60%

basics

~20 s

With Nuxt 4's default ssr: true, each request gets server-rendered HTML plus a payload for hydration. ssr: false sends a shell with an empty #__nuxt root for the browser to fill. nuxt generate prerenders pages into static HTML files at build time.

open as a page

In Nuxt 4, what is the difference between a handler in `server/api/` and one in `server/routes/`, and how do file names become URLs?

level: juniorimportance: must knowfreq 44%

basics

~20 s

Both folders hold Nitro handlers exported with defineEventHandler. Files in server/api are served under /api, files in server/routes at the bare path; brackets in a file name become parameters and a .get or .post suffix limits the method.

open as a page

In Nuxt 4, why does a counter seeded with a random value in a plain ref mismatch after hydration, while useState keeps the server's value?

level: middleimportance: must knowfreq 48%

basics

~20 s

A plain ref(Math.random()) is re-created when setup runs again in the browser, so the client value differs from the server HTML. useState stores its value in the Nuxt payload; during hydration it finds that value and skips the initialiser, so both sides match.

open as a page

In Nuxt 4's `useFetch`, what do `lazy: true`, `server: false`, `immediate: false` and `enabled: false` each change, and what does `status` show?

level: middleimportance: must knowfreq 52%

basics

~20 s

lazy: true stops client navigation waiting on the data; server: false moves the fetch to the browser after hydration; immediate: false waits for execute(); enabled: false (Nuxt 4.5+) blocks every run. status shows idle, pending, success or error.

open as a page

In Nuxt 4, when do you use `runtimeConfig` versus `app.config.ts`, and which of their values reach the browser?

level: middleimportance: must knowfreq 45%

basics

~20 s

runtimeConfig holds values that differ per deployment and can be overridden by NUXT_ environment variables; only its public section reaches the browser. app.config.ts holds build-time, non-secret settings such as theme or title, and all of it is bundled into the client.

open as a page

In Nuxt 4, how do routeRules let one app prerender marketing pages, server-render product pages, cache the blog and render the dashboard client-only?

level: middleimportance: must knowfreq 52%

basics

~20 s

In Nuxt 4, routeRules in nuxt.config.ts map URL patterns to behaviour: prerender: true for marketing pages, no rule for SSR product pages, isr or swr for the blog, and ssr: false for /dashboard/**. The app is built with nuxt build.

open as a page

In a Nuxt 4 server route, how do you read the path parameter, query and JSON body, and reject bad input?

level: middleimportance: must knowfreq 40%

basics

~20 s

Use getRouterParam for the path parameter, getQuery for the query (values arrive as strings) and await readBody for the body, or their validated variants that answer 400. Reject input by throwing createError with status and statusText.

open as a page

In a Nuxt 4 auth route middleware, navigateTo('/login') is called but not returned. Why do signed-out users still reach /account, and what does returning it change?

level: seniorimportance: must knowfreq 50%

basics

~20 s

Inside Nuxt route middleware, navigateTo only produces a redirect target; the middleware's return value decides the navigation. Unreturned, the middleware returns undefined and /account renders; returned, Nuxt redirects to /login, as a 302 during the server render.

open as a page

In Nuxt 4, how do you add a module in `nuxt.config.ts` and pass it options, and when does that configuration run?

level: juniorimportance: should knowfreq 38%

basics

~20 s

List it in the modules array of nuxt.config.ts as a package name, a path, a [name, options] tuple or an inline function, or set options under the module's config key. The file runs in Node when nuxt dev or nuxt build starts.

open as a page

In Nuxt 4, what key does useState get when you omit one, what if two composables reuse a key, and which values can it hold?

level: middleimportance: should knowfreq 34%

basics

~20 s

Without a key, Nuxt's compiler appends one unique to that call site in the file. Two calls with the same key share one state, even from unrelated composables. Values must survive payload serialisation: no class instances, functions or symbols.

open as a page

In Nuxt 4, how do you make a flight-status page's `useFetch` refetch when the airport filter changes, and what happens to its key?

level: middleimportance: should knowfreq 44%

basics

~20 s

Pass the filter reactively, as query: { airport } or a getter URL, not a string built from airport.value. The auto-key hashes the URL and query, so each airport gets its own entry and a change starts a new fetch; watch: false opts out.

open as a page

In Nuxt 4, what does definePageMeta do, and why can it not use a ref declared in the same script setup?

level: middleimportance: should knowfreq 46%

basics

~20 s

definePageMeta is a Nuxt compiler macro that attaches route metadata such as middleware, layout, validate or key to a page in app/pages/. The build hoists its object out of the component, so it can use imports and pure functions but not setup state.

open as a page

In Nuxt 4, when does a page file beside a same-named folder become a parent route, and what must that parent render?

level: middleimportance: should knowfreq 42%

basics

~10 s

In Nuxt 4, app/pages/account.vue plus an app/pages/account/ folder makes account.vue a parent route whose folder files become children. The parent must render <NuxtPage /> where the child appears, or /account/orders shows only the parent.

open as a page

In Nuxt 4, how does `NUXT_PUBLIC_SITE_URL` override `runtimeConfig.public.siteUrl` at runtime, and why does `siteUrl: process.env.SITE_URL` stop working after deployment?

level: middleimportance: should knowfreq 36%

basics

~10 s

At runtime Nuxt maps each existing runtimeConfig key to an upper-snake-case NUXT_ variable, so public.siteUrl reads NUXT_PUBLIC_SITE_URL. process.env.SITE_URL in nuxt.config is read once at build time and frozen into the output.

open as a page

In Nuxt 4, what does <ClientOnly> render on the server and during hydration, and when is it the right tool for a browser-only widget?

level: middleimportance: should knowfreq 42%

basics

~20 s

Nuxt 4's <ClientOnly> renders its fallback (the #fallback slot, or fallback text in a span) on the server and again during hydration, then swaps in its default slot after mounting. It suits a browser-only widget inside an otherwise server-rendered page.

open as a page

In a Nuxt 4 team app, two files in app/composables/ both export useCart. What breaks, and how do you scope or disable auto-imports to fix it?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Two exports named useCart give Nuxt 4's auto-import two candidates for one name, so a page can get the other team's version. Rename by domain or import explicitly; imports.scan: false stops scanning your folders, and imports.autoImport: false turns injection off.

open as a page

In Nuxt 4, what does useNuxtApp() give a composable, and why can a Nuxt composable called after an await throw NUXT_E1001 during SSR only?

level: seniorimportance: should knowfreq 31%

basics

~20 s

useNuxtApp() returns the current Nuxt app: vueApp, provided helpers, hooks, payload, ssrContext and isHydrating. On the server each request has its own app tracked implicitly; after an await in a plain function that tracking is lost, so the next Nuxt composable throws NUXT_E1001.

open as a page

In Nuxt 4, why does a flight board's refresh button stop fetching after its `useFetch` gains a `getCachedData` returning `nuxtApp.payload.data[key]`, and how do you fix it?

level: seniorimportance: should knowfreq 27%

basics

~20 s

In Nuxt 4 getCachedData runs before every fetch, including refresh() and watcher runs, and any non-undefined return skips the request. The payload always holds the last result, so the refresh is served from cache. Return undefined when ctx.cause is refresh:manual or refresh:hook.

open as a page

After upgrading a flight-status page from Nuxt 3 to Nuxt 4, why does `data.value.flights.push()` stop updating the list and `data.value === null` never match?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Nuxt 4 returns data as a shallowRef, so mutating nested properties triggers no update; replace the value or pass deep: true. data and error now start as undefined instead of null, so null checks never match; test status or supply a default.

open as a page

In Nuxt 4, a flight header and a flight timeline both call `useFetch` for the same flight: one request or two, and what do they share?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Two, unless both pass the same explicit key, because useFetch auto-keys per call site. With a shared key, Nuxt 4 keeps one entry whose data, error and status refs both read; transform and default must match, and the entry is purged after its last consumer unmounts.

open as a page

In Nuxt 4, should a recipe site's auth check be app/middleware/auth.global.ts or a named auth middleware on account.vue, and what ordering and double-run effects follow?

level: seniorimportance: should knowfreq 38%

basics

~20 s

For one protected /account area, a named app/middleware/auth.ts set on account.vue with definePageMeta covers every /account child and skips public recipe pages. A .global file runs on every navigation, alphabetically before page middleware, and must exempt /login.

open as a page

You are publishing a Nuxt 4 analytics module. How do you structure it with `defineNuxtModule`, and how do its options reach the plugin, composable and component it adds?

level: seniorimportance: should knowfreq 27%

basics

~20 s

Export defineNuxtModule with meta (name, configKey, compatibility), defaults and a setup that registers runtime files with addPlugin, addImports, addComponent and addServerHandler. Options exist only at build time, so setup copies what runtime code needs into runtimeConfig.

open as a page

In Nuxt 4, where and how often does a `defineNuxtPlugin` plugin run, and how do you load an analytics client only in the browser and expose `$track`?

level: seniorimportance: should knowfreq 32%

basics

~20 s

A Nuxt plugin runs as the Nuxt app is created: on the server for every request, in the browser once per page load. A .client file name keeps it out of SSR, and returning { provide: { track } } exposes $track.

open as a page

A Nuxt 4 app's private analytics API key appeared in its page source. Which config, module and plugin mistakes put it there, and how do you keep it server-side?

level: seniorimportance: should knowfreq 34%

basics

~20 s

It got there through runtimeConfig.public, app.config, a module copying options into public config, or server code rendering it or storing it in payload state. Keep it a private runtimeConfig key read only by server code, which the browser calls.

open as a page

A Nuxt 4 blog on a self-hosted Node server uses routeRules isr: 3600, yet every request re-renders. Why, and would swr or prerender fit better?

level: seniorimportance: should knowfreq 38%

basics

~20 s

On a Node server, Nitro caches rendered pages only for swr or cache rules; isr is left to hosting presets with a platform cache, so alone it caches nothing. Self-hosted, use swr: 3600; prerender suits a blog rebuilt on each publish.

open as a page

In Nuxt 4, what does routeRules '/dashboard/**': { ssr: false } change for the dashboard, and what do you give up compared with SSR?

level: seniorimportance: should knowfreq 33%

basics

~20 s

With '/dashboard/**': { ssr: false }, Nuxt 4 answers dashboard URLs with an app shell: an empty #__nuxt root plus the optional loading template. Middleware, data fetching and rendering move to the browser, at the cost of indexable HTML and an early first paint.

open as a page

In Nuxt 4, an inventory proxy is wrapped in `defineCachedEventHandler(handler, { maxAge: 60 })`. What happens on a miss, on a hit, and after 60 seconds?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A miss runs the handler and stores its response in Nitro's cache storage; a hit replays it without calling upstream. After 60 seconds the default swr: true serves the stale response while a background refresh replaces it.

open as a page

showing 1–30 of 33