unimport vs unplugin-auto-import vs babel-plugin-auto-import: Auto-Importing in JavaScript (2026)
TL;DR
unimport is the UnJS auto-import engine — scans for available exports and generates import statements automatically, powers Nuxt's auto-imports (composables, utils, components). unplugin-auto-import is the universal build plugin — uses unplugin to work with Vite, Webpack, Rollup, and esbuild, auto-imports from libraries like Vue, React, and custom directories. babel-plugin-auto-import is the Babel transform — resolves missing identifiers at compile time, works with any Babel-based setup. In 2026: unplugin-auto-import for Vite/Webpack projects, unimport for framework authors building Nuxt-like DX, babel-plugin-auto-import for Babel-only setups.
Key Takeaways
- unimport: ~5M weekly downloads — UnJS, powers Nuxt auto-imports, generates TypeScript declarations
- unplugin-auto-import: ~3M weekly downloads — universal plugin (Vite/Webpack/Rollup), preset-based
- babel-plugin-auto-import: ~50K weekly downloads — Babel transform, simple identifier mapping
- Auto-importing eliminates repetitive
import { ref, computed } from "vue"boilerplate - unimport and unplugin-auto-import generate
.d.tsfiles for TypeScript support - unplugin-auto-import has presets for Vue, React, Svelte, VueUse, and more
The Problem
// Without auto-imports — every file starts with boilerplate:
import { ref, computed, watch, onMounted } from "vue"
import { useRouter, useRoute } from "vue-router"
import { storeToRefs } from "pinia"
import { useUserStore } from "@/stores/user"
import { formatDate } from "@/utils/date"
import { debounce } from "@/utils/helpers"
// With auto-imports — just use them:
const count = ref(0)
const doubled = computed(() => count.value * 2)
const router = useRouter()
const { user } = storeToRefs(useUserStore())
const formatted = formatDate(new Date())
unimport
unimport — the auto-import engine:
Basic usage
import { createUnimport } from "unimport"
const { injectImports } = createUnimport({
// Import from packages:
imports: [
{ name: "ref", from: "vue" },
{ name: "computed", from: "vue" },
{ name: "watch", from: "vue" },
{ name: "useRouter", from: "vue-router" },
],
})
// Transform source code — adds missing imports:
const input = `
const count = ref(0)
const doubled = computed(() => count.value * 2)
`
const { code } = await injectImports(input)
// Output:
// import { ref, computed } from "vue"
// const count = ref(0)
// const doubled = computed(() => count.value * 2)
Presets
import { createUnimport } from "unimport"
import { builtinPresets } from "unimport"
const { injectImports } = createUnimport({
presets: [
// Built-in presets for popular frameworks:
"vue", // ref, computed, watch, onMounted, etc.
"vue-router", // useRouter, useRoute, etc.
"pinia", // defineStore, storeToRefs, etc.
"react", // useState, useEffect, useCallback, etc.
"svelte", // onMount, onDestroy, etc.
],
})
Directory scanning
import { createUnimport } from "unimport"
const { injectImports } = createUnimport({
// Auto-import from local directories:
dirs: [
"./src/composables", // useAuth, useTheme, etc.
"./src/utils", // formatDate, debounce, etc.
"./src/stores", // useUserStore, useCartStore, etc.
],
// Options for directory scanning:
dirsScanOptions: {
filePatterns: ["*.ts", "*.js"],
fileFilter: (file) => !file.includes(".test."),
},
})
TypeScript declaration generation
import { createUnimport } from "unimport"
const unimport = createUnimport({
imports: [
{ name: "ref", from: "vue" },
{ name: "computed", from: "vue" },
],
dirs: ["./src/composables"],
})
// Generate .d.ts for TypeScript support:
const dts = await unimport.generateTypeDeclarations()
// Writes: declare global { const ref: typeof import("vue")["ref"] }
// Generate ESLint globals config:
const eslintConfig = await unimport.generateESLintFlatConfig()
How Nuxt uses unimport
// Nuxt's auto-imports are powered by unimport:
// nuxt.config.ts — these are auto-imported without manual imports:
// useAsyncData, useFetch, useHead, useRoute, useRouter
// ref, computed, watch, onMounted
// definePageMeta, navigateTo, useState
// In your Nuxt component — no imports needed:
const { data } = await useFetch("/api/packages")
const route = useRoute()
const count = ref(0)
// Nuxt configures unimport with:
// - Vue composables (ref, computed, etc.)
// - Nuxt composables (useFetch, useState, etc.)
// - Auto-scanned composables/ directory
// - TypeScript declarations generated automatically
unplugin-auto-import
unplugin-auto-import — universal auto-import plugin:
Vite setup
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite"
export default defineConfig({
plugins: [
AutoImport({
// Presets for popular libraries:
imports: [
"vue",
"vue-router",
"pinia",
"@vueuse/core",
],
// Auto-import from directories:
dirs: [
"./src/composables",
"./src/stores",
],
// Generate TypeScript declarations:
dts: "./auto-imports.d.ts",
// Generate ESLint config:
eslintrc: {
enabled: true,
},
}),
],
})
Webpack setup
// webpack.config.js
const AutoImport = require("unplugin-auto-import/webpack")
module.exports = {
plugins: [
AutoImport({
imports: ["react", "react-router-dom"],
dts: "./auto-imports.d.ts",
}),
],
}
React preset
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite"
export default defineConfig({
plugins: [
AutoImport({
imports: [
"react",
// Custom imports:
{
"react-router-dom": [
"useNavigate",
"useParams",
"useSearchParams",
"Link",
],
"@tanstack/react-query": [
"useQuery",
"useMutation",
"useQueryClient",
],
},
],
dts: true,
}),
],
})
// Now in any component — no imports needed:
function PackageList() {
const [search, setSearch] = useState("")
const navigate = useNavigate()
const { data } = useQuery({
queryKey: ["packages", search],
queryFn: () => fetchPackages(search),
})
// useState, useNavigate, useQuery all auto-imported
}
Custom resolvers
import AutoImport from "unplugin-auto-import/vite"
export default defineConfig({
plugins: [
AutoImport({
imports: ["vue"],
// Custom resolvers for complex cases:
resolvers: [
// Auto-import from a UI library:
(name) => {
if (name.startsWith("El"))
return { name, from: "element-plus" }
},
],
// Ignore specific auto-imports:
ignore: ["useFetch"],
// Transform options:
include: [
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/,
/\.vue\?vue/,
],
}),
],
})
VueUse preset
import AutoImport from "unplugin-auto-import/vite"
export default defineConfig({
plugins: [
AutoImport({
imports: [
"vue",
"@vueuse/core",
"@vueuse/head",
],
dts: true,
}),
],
})
// All VueUse composables available without imports:
const { x, y } = useMouse()
const isDark = useDark()
const { copy } = useClipboard()
const { isFullscreen, toggle } = useFullscreen()
babel-plugin-auto-import
babel-plugin-auto-import — Babel-based auto-import:
Basic setup
// babel.config.json
{
"plugins": [
["auto-import", {
"declarations": [
{ "default": "React", "path": "react" },
{ "members": ["useState", "useEffect", "useCallback"], "path": "react" },
{ "members": ["useNavigate", "Link"], "path": "react-router-dom" }
]
}]
]
}
Usage
// Input (no imports):
function App() {
const [count, setCount] = useState(0)
const navigate = useNavigate()
return <div onClick={() => setCount(count + 1)}>{count}</div>
}
// Babel transforms to:
import React, { useState } from "react"
import { useNavigate } from "react-router-dom"
function App() {
const [count, setCount] = useState(0)
const navigate = useNavigate()
return <div onClick={() => setCount(count + 1)}>{count}</div>
}
Limitations
babel-plugin-auto-import:
✅ Works with any Babel setup
✅ Simple configuration
✅ No build tool dependency
❌ No directory scanning
❌ No TypeScript declarations
❌ No presets — must list every import manually
❌ No ESLint integration
❌ Babel-only — doesn't work with esbuild/SWC
❌ Not actively maintained
For most projects: use unplugin-auto-import instead
Feature Comparison
| Feature | unimport | unplugin-auto-import | babel-plugin-auto-import |
|---|---|---|---|
| Build tool support | Programmatic | Vite, Webpack, Rollup, esbuild | Babel only |
| Presets | Vue, React, Svelte, etc. | Vue, React, Svelte, VueUse, etc. | ❌ (manual) |
| Directory scanning | ✅ | ✅ | ❌ |
| TypeScript .d.ts | ✅ | ✅ | ❌ |
| ESLint config | ✅ | ✅ | ❌ |
| Custom resolvers | ✅ | ✅ | ❌ |
| Framework use | Nuxt core | Plugin for any project | Babel projects |
| API level | Low-level engine | Build plugin | Babel transform |
| Weekly downloads | ~5M | ~3M | ~50K |
When to Use Each
Use unimport if:
- Building a framework with Nuxt-like auto-import DX
- Need a programmatic auto-import engine
- Want full control over import injection and scanning
- Building custom tooling that needs import resolution
Use unplugin-auto-import if:
- Want auto-imports in your Vite, Webpack, or Rollup project
- Need presets for Vue, React, VueUse, etc.
- Want TypeScript declarations and ESLint config generated
- Building an app (not a framework)
Use babel-plugin-auto-import if:
- Stuck with a Babel-only build pipeline
- Need simple identifier-to-import mapping
- Can't use build plugins (legacy setup)
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on unimport v3.x, unplugin-auto-import v0.18.x, and babel-plugin-auto-import v1.x.
Compare auto-import tools and developer utilities on PkgPulse →