Open-source project
unplugin/unplugin-auto-import avatar
unplugin/unplugin-auto-import

unplugin-auto-import: on-demand API imports for Vite, Webpack and Rollup

Auto import APIs on-demand for Vite, Webpack and Rollup

3,799 stars218 forksTypeScriptMIT

At a glance

What is it?
A build-time transform that rewrites your source so that ref, computed and useState resolve without import statements, and emits a .d.ts file so TypeScript still sees them. It is convenient, and it makes every global in your project invisible to a reader.
Who is it for?
Adopt unplugin-auto-import if you already write Vue or React code and want to stop repeating the same import lines across hundreds of files, and if your team accepts that identifiers will resolve without a visible import. Skip it if you need each file to be readable in isolation, if you are on Nuxt (the plugin is already built in, per the README), or if you cannot run Node 20.19.0 or newer, which package.json lists under engines.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 62 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What unplugin-auto-import removes from your source files

The README frames the whole project as a before-and-after pair. Without the plugin, a Vue component opens with `import { computed, ref } from 'vue'` before any logic. With it, the same file starts at `const count = ref(0)`. The React example is identical in shape: `import { useState } from 'react'` disappears and `useState(0)` stays.

That is the entire value proposition, and it is narrower than the name suggests. The plugin does not move code between modules or resolve circular dependencies. It deletes a line you would otherwise type, in every file, forever. On a codebase with a few hundred components that is a real amount of typing, and it removes a class of merge conflict that only exists because two branches edited the same import block.

The cost is that a reader of any single file can no longer tell where `ref` comes from. The information lives in the build config, not the source. Teams that review code across many small files feel this more than teams that work in one large module at a time.

How the unplugin build-time transform actually works

unplugin-auto-import is a wrapper around unplugin, which is the compatibility layer that lets one plugin ship entry points for Vite, Webpack, Rollup, Rspack, Rolldown, esbuild and Astro. The package.json exports map confirms this: there are separate subpath exports for `./vite`, `./webpack`, `./rollup`, `./rolldown`, `./rspack`, `./esbuild`, `./astro`, `./nuxt` and `./types`, each pointing at its own `.mjs` file in `dist`.

The mechanism is a source transform, not a runtime shim. The plugin matches files against the `include` array, which by default covers `.ts`, `.tsx`, `.js`, `.jsx`, `.vue`, `.vue?vue`, `.vue.[tj]sx?vue` and `.md`. For each matched file it resolves the identifiers you listed under `imports` and `dirs`, and injects the corresponding import statements into the module before the bundler parses it. Nothing is added to the global scope at runtime. The generated code is ordinary ESM imports, which is why tree shaking and `sideEffects: false` in package.json still behave normally.

The `imports` field accepts preset names as bare strings ('vue', 'vue-router') and object entries for anything else. Inside an object entry, a plain string is a named import, a two-element array is an alias, and `['default', 'axios']` produces `import { default as axios } from 'axios'`. A separate object form with `from`, `imports` and `type: true` registers type-only imports, which the README shows for `RouteLocationRaw` from vue-router.

Two options change the shape of the output rather than the set of imports. `injectAtEnd` controls whether injected imports land after your existing import statements, and `viteOptimizeDeps` feeds the auto-imported packages into Vite's dependency pre-bundling, which the README marks as recommended to enable.

Installing unplugin-auto-import and wiring the Vite plugin

The package is a dev dependency. The README gives the install command directly:

bash
npm i -D unplugin-auto-import

The same package exposes a different import path per bundler, so the config file decides which one you get. For Vite, the README shows this shape:

ts
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [
    AutoImport({ /* options */ }),
  ],
})

Webpack and Rspack use CommonJS `require` instead: `require('unplugin-auto-import/webpack')({ /* options */ })` and `require('unplugin-auto-import/rspack')({ /* options */ })`. Rollup, Rolldown, esbuild and Astro each have their own subpath, and the README notes that Nuxt does not need the plugin at all because it is already built in.

A first real configuration names the presets you want and where the type declarations should go:

ts
AutoImport({
  imports: ['vue', 'vue-router'],
  dts: './auto-imports.d.ts',
})

After the first build or dev-server start, a file appears at that path. If `typescript` is installed locally and you leave `dts` unset, the default is `./auto-imports.d.ts`. Opening it is the fastest way to confirm the transform ran: it should contain declarations for the APIs from the presets you listed. Setting `dts: false` disables generation entirely, which is what you want if you have decided to keep the globals out of your type layer.

Auto-importing your own directories, and the scanning rules that bite

Presets cover third-party APIs. The `dirs` option covers your own code, and its defaults are the part most likely to surprise you. The README states that by default only one level of modules under the directory is scanned. `'./composables'` picks up root modules only; `'./composables/**'` is needed for nested ones. A directory scanned one level deep will silently miss a composable that lives in a subfolder, and the failure appears as an undefined identifier at build time, not as a warning from the plugin.

The object form of each `dirs` entry carries its own `types` flag, and the README shows using it to switch type importing off for a single directory even when `dirsScanOptions.types` is on globally. Scanning behaviour is further controlled by `dirsScanOptions.filePatterns` and `dirsScanOptions.fileFilter`, which take glob patterns and a predicate function respectively. `defaultExportByFilename` is off by default; turning it on enables auto import by filename for default module exports under directories, which is a meaningfully different resolution rule and worth reading twice before enabling.

Type generation has its own knobs. `dtsMode` defaults to 'append', meaning new definitions are added to an existing declaration file and older ones are kept. The alternative, 'overwrite', replaces the file wholesale. Append is the safer default for incremental work but it means a stale declaration for a composable you deleted can survive in the file until someone switches modes or removes the file by hand. `dtsPreserveExts` controls whether `.ts` and `.tsx` extensions are kept in the generated declarations, and `ignoreDts` takes strings or regexes for imports you want excluded from declaration generation, which the README suggests for cases where you need to supply a custom signature yourself.

ESLint, templates and the places unplugin-auto-import gets in the way

The plugin writes an ESLint globals file when you enable `eslintrc`, and the README notes the default is `false`. That default matters. Turn it on and you get a generated JSON file listing the auto-imported names so ESLint's no-undef rule stops complaining. Leave it off and every auto-imported identifier is an undefined variable as far as your linter is concerned. Neither state is wrong, but the mismatch between what the bundler accepts and what the linter accepts is the single most common source of confusion with this style of setup.

Vue templates are a separate switch. `vueTemplate` defaults to `false`, so identifiers used only inside a `<template>` block are not auto-imported unless you enable it. `vueDirectives` is `undefined` by default and controls auto import of directives inside templates. Both are opt-in for a reason: template-level auto import widens the set of names that resolve without an import, and the README links to upstream unimport pull requests rather than documenting the behaviour in detail.

The `ignore` option takes an array of strings or regexes naming imports to filter out, and `ignoreDts` does the same for the declaration file. If a name is auto-imported in your source but excluded from the declarations, TypeScript will report it as unknown even though the build succeeds. That gap between build-time resolution and type-time resolution is the failure mode to watch for.

The other real limitation is environmental. package.json sets `engines.node` to `>=20.19.0`. Older Node versions are outside what the package declares support for.

unplugin-auto-import compared with unplugin-vue-components

The two plugins are frequently installed together and are frequently confused, so the difference is worth stating plainly. unplugin-auto-import handles APIs: functions, composables and refs that you call inside script code. unplugin-vue-components handles components: the tags you write in a template. The README notes that the `resolvers` option is compatible with unplugin-vue-components, and that compatibility is the seam where the two meet, since a resolver decides how a name resolves to a module.

If your problem is that every file repeats `import { ref, computed } from 'vue'`, the components plugin does nothing for you. If your problem is that every file repeats `import MyButton from './MyButton.vue'`, the auto-import plugin does nothing for you. Installing both is normal, but they solve different halves of the same annoyance, and a team that installs one expecting the other will conclude the tool does not work.

Nuxt is the other comparison worth making. The README says you do not need this plugin for Nuxt because it is already built in. Adding it on top of Nuxt duplicates a capability the framework already provides, and the package.json peer dependency on `@nuxt/kit` exists for the `./nuxt` export path rather than as a requirement for ordinary Vite or Webpack use.

Editorial conclusion

Adopt unplugin-auto-import if you already write Vue or React code and want to stop repeating the same import lines across hundreds of files, and if your team accepts that identifiers will resolve without a visible import. Skip it if you need each file to be readable in isolation, if you are on Nuxt (the plugin is already built in, per the README), or if you cannot run Node 20.19.0 or newer, which package.json lists under engines. Before committing, run the build once and open the generated auto-imports.d.ts to confirm the presets you listed actually produced declarations, then check whether your ESLint config needs the eslintrc output so lint does not flag the new globals as undefined.

Frequently asked questions

How do I install unplugin-auto-import?

Install it as a dev dependency with npm i -D unplugin-auto-import, then import the subpath that matches your bundler, such as unplugin-auto-import/vite, and add it to the plugins array. The package.json engines field requires Node 20.19.0 or newer.

Does unplugin-auto-import work with React as well as Vue?

Yes. The README shows a React example where useState is used without an import statement, and the repository includes example directories for vite-react, vite-svelte, solid-js and vite-astro alongside the Vue playground.

Why does ESLint report my auto-imported names as undefined?

The eslintrc option defaults to false, so no ESLint globals file is generated until you enable it. Turning it on produces a JSON file listing the auto-imported names so the linter recognises them.

Do I need unplugin-auto-import in a Nuxt project?

The README states that you do not need this plugin for Nuxt because it is already built in.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. unplugin/unplugin-auto-import on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/unplugin-unplugin-auto-import.svg)](https://hysenlabs.com/projects/unplugin-unplugin-auto-import)