In a Nuxt 4 app, what is auto-imported without an import line, and how does a function in app/composables/ get picked up?
answer
- three scanned app directories
- Vue and Nuxt APIs come built in
- top-level files only
- default export takes the file name
- #imports and .nuxt/imports.d.ts
basics
~20 sNuxt 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 sNuxt 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// 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
Recall the sources: Vue APIs, Nuxt composables, and the top-level files of app/composables/, app/utils/ and app/components/.
Explain the scanning rules: named versus default exports, why nested folders are skipped, and how #imports and generated types fit in.
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.
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