How do auto-imports work in Nuxt?
Auto-imports in Nuxt - a build-time feature that makes composables, components, and utilities available in every file of your app without writing a single import statement.
Theory
TL;DR
- Nuxt scans
~/composables,~/components,~/utilsat build time and injects their exports into every.vuefile - Think hotel kitchen: tools sit in shared drawers, chefs grab what they need without asking
- No
importboilerplate; the Vite plugin handles injection via a virtual#importsmodule - To opt out: set
autoImports: falseinnuxt.config.ts
Quick example
<!-- pages/users.vue - zero import lines needed -->
<script setup>
// useFetch and useRoute come from the auto-generated #imports module
const { data: users } = await useFetch('/api/users')
const route = useRoute()
</script>
<template>
<!-- ~/components/UserList.vue is picked up automatically -->
<UserList v-for="user in users" :key="user.id" :user="user" />
</template>useFetch, useRoute, and UserList are all injected without a single import line. Nuxt resolves them at build time, not at runtime.
How it works internally
Nuxt's Vite plugin scans your directories at build time and generates a virtual #imports module. That module exports everything it found: composables, utility functions, components. Every .vue file in the project gets this module injected into its scope automatically.
At runtime, this is just standard JS modules. No overhead, no magic. The scanning and injection happen once during the build, and the result is identical to writing the imports yourself.
Default scan targets: ~/composables, ~/components, ~/utils, ~/plugins, and ~/server/composables (added in Nuxt 3.8+). You can extend the list via imports.dirs in nuxt.config.ts.
Client vs server auto-imports
Client composables go in ~/composables/. Server composables go in ~/server/composables/. The .client.ts and .server.ts file suffixes tell Nuxt which build to include a file in.
Put a client composable inside a server API route and Nuxt throws "not defined". The server build never scanned ~/composables/. This is the most common source of auto-import bugs I've seen in Nuxt projects. Moving the file to ~/server/composables/ fixes it.
When to use
- Adding a new page or composable: create the file, it works everywhere immediately
- Shared logic across components: drop it in
~/composables/, done - Large monorepo or strict bundle control: disable with
autoImports: false, add manual imports
Common mistakes
Client composable in a server route:
// server/api/orders.get.ts - WRONG
// ~/composables/ is not scanned during the server build
const data = useMyClientUtil() // throws: not defined
// CORRECT - move to ~/server/composables/useMyServerUtil.tsNaming your composable the same as a Nuxt built-in:
// ~/composables/useRoute.ts
// This shadows Nuxt's useRoute and breaks routing app-wide
// Rename to useAppRoute or usePageRouteUsing auto-imports inside defineNuxtPlugin:
// Plugins run before auto-import scan completes
export default defineNuxtPlugin(() => {
const util = useMyUtil() // undefined - scan has not happened yet
// Fix: inject via provide instead
return {
provide: { myUtil: () => 'works' }
}
})Real-world usage
- Nuxt Content:
queryContent('/')auto-imported for MDX blog pages (nuxt/content v2.6+) - Nuxt UI:
<UButton />from~/components/Uiavailable everywhere (nuxt/ui v2.8) - Supabase Nuxt:
useSupabaseClient()anduseSupabaseUser()for auth flows - Vitest + Nuxt: test setup auto-mocks the
#importsmodule
Follow-up questions
Q: What directories does Nuxt scan by default?
A: ~/composables, ~/components, ~/utils, ~/plugins, and ~/server/composables. Add custom paths via imports.dirs in nuxt.config.ts.
Q: How do you add a custom directory to auto-imports?
A: Set imports: { dirs: ['~/logic'] } in nuxt.config.ts. Every exported function from that folder becomes auto-imported across the app.
Q: How do client and server auto-imports differ?
A: Client composables live in ~/composables/, server ones in ~/server/composables/. The .client.ts or .server.ts suffix restricts a file to one build target only.
Q: Does disabling auto-imports improve bundle size?
A: Auto-imported code is already tree-shaken, so unused composables do not end up in the bundle. You would disable auto-imports for explicit dependency graphs in a large monorepo, not for bundle size.
Q: How does this differ from Vue's globalProperties?
A: Auto-imports are module exports - tree-shakable and TypeScript-inferred. globalProperties are runtime objects attached to the app instance with no tree-shaking and much weaker type support.
Examples
Basic: composable and component on a page
<!-- pages/dashboard.vue -->
<script setup>
const route = useRoute() // auto-imported
const { data: orders } = await useFetch('/api/orders', {
params: { userId: route.params.id }
})
</script>
<template>
<!-- ~/components/OrderTable.vue - auto-imported -->
<OrderTable :orders="orders" />
</template>useFetch, useRoute, and OrderTable are all resolved by Nuxt at build time. Rename the component file and the tag name in templates updates automatically.
Intermediate: server composable vs client composable
// ~/server/composables/useDb.ts - available in server/api/ routes only
export const useDb = () => {
return { query: (sql: string) => db.execute(sql) }
}
// server/api/users.get.ts
export default defineEventHandler(async () => {
const { query } = useDb() // auto-imported from ~/server/composables/
return query('SELECT * FROM users')
})
// ~/composables/useUsers.ts - available in pages/ and components/ only
export const useUsers = () => {
const users = ref([])
const load = async () => {
users.value = await $fetch('/api/users')
}
return { users, load }
}The server and client builds each have their own auto-import scope. Mixing them is where "X is not defined" errors come from.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.