Skip to content

Auto Import ​

Overview ​

Auto import in Vue refers to a feature that automatically imports components, plugins, modules or other resources into your Vue application without the need for manually specifying each import statement at the top of your files. This can greatly simplify codebase and reduce boilerplate, especially in large projects with many components.

There are side effects of using this technique. Some impact on build server start and reload time, as plugin scans all files. And dependencies are not clearly visible. Nevertheless, the positive impact on development speed is significant.

Routes ​

Check File Based Routing.

Composables ​

  1. Install unplugin libraries
bash
pnpm i -D unplugin-auto-import
  1. Add auto import configuration to vite.config.ts
ts
// ...
import AutoImport from 'unplugin-auto-import/vite'
// ...
plugins: [
  // ...
  AutoImport({
    imports: [
      'vue',
      'vue-router',
      'vue-i18n',
      {
        from: 'vuetify',
        imports: [
          'useDisplay',
          'useDate',
          'useDefaults',
          'useDisplay',
          'useGoTo',
          'useLayout',
          'useLocale',
          'useRtl',
          'useTheme',
        ],
      },
    ],
    dirs: ['./src/composables/**', './src/stores/**', './src/components/**'],
  }),
]
//...

This will create file auto-imports.d.ts containing auto imports for:

  • vue, vue-router and i18n
  • all composables for vuetify
  • all composables from @/composables folder.
  1. Add this file to tsconfig.app,json
json
...
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "cypress", "./cypress.d.ts", "./auto-imports.d.ts"],
...
  1. Modify @/pages/sandbox/index.vue - remove all imports, it will still work.

NOTE

VsCode and dev restart might be needed for these changes to start working.

vue
<template>
  <v-card :style="cardBackground">
    <v-card-title><v-icon icon="$mdiHome" />{{ t('sandbox.title') }}</v-card-title>
    <v-card-text>{{ t('sandbox.description') }}</v-card-text>
    <v-card-actions>
      <v-btn @click="console.log('Primary!')" color="primary">Primary</v-btn>
      <v-btn @click="console.log('Secondary!')" color="secondary">Secondary</v-btn>
      <v-btn @click="settings.toggleTheme()">Toggle theme</v-btn>
    </v-card-actions>
  </v-card>
</template>

<script setup lang="ts">
import { useCardBackground } from '@/composables/ui'
import { useI18n } from 'vue-i18n'
import { useSettingsStore } from '@/stores/app/settings'

const cardBackground = useCardBackground('#0000ff')
const { t } = useI18n()
const settings = useSettingsStore()
</script>

Components ​

  1. Install unplugin-vue-components
bash
pnpm i unplugin-vue-components -D
  1. Add component auto import in ./vite.config.ts
ts
// ...
import Components from 'unplugin-vue-components/vite'
// ...
export default defineConfig({
  plugins: [
// ...
    Components({})
// ...
  ],
})
  1. Test

Create @/components/TestComponent.vue

vue
<template>I am a test component.</template>

And use it anywhere, e.g. in @/App.vue without any importing.

vue
<template>
  <!-- -->
   <test-component />
  <!-- -->
</template>

Icons ​

As on moment there is no native support yet - see Feature request, here is a custom plugin to generate icon includes based on $mdi pattern.

  1. Create a plugin file

@/plugins/icons.ts ​

source
ts
import { promises as fs } from 'node:fs'
import type { Plugin, ResolvedConfig } from 'vite'

type AutoImportMdiIconsOptions = {
  dirs?: string[]
  exts?: string[]
  pattern?: string
  outputPath?: string
  outputFile?: string
  log?: boolean
}

export function AutoImportMdiIcons(options?: AutoImportMdiIconsOptions): Plugin {
  let config: ResolvedConfig
  const dirs: string[] = options?.dirs || ['./src']
  const exts: string[] = options?.exts || ['.vue', '.ts', '.md']
  const pattern = options?.pattern || '$mdi'
  const outputPath = options?.outputPath || './src/themes/'
  const outputFile = options?.outputFile || 'icons.ts'
  const log = options?.log || true

  function pluginLogger(message: string): void {
    if (!log) return
    const date = new Date()
    const time = date.toLocaleTimeString('en-US', {
      hour: 'numeric',
      minute: '2-digit',
      second: '2-digit',
      hour12: true,
    })
    console.log(
      '\x1b[90m' +
        time +
        '\x1b[0m' +
        '\x1b[36m' +
        ' [Auto Import MDI Icons] ' +
        '\x1b[0m' +
        message,
    )
  }

  async function autoImportVuetifyMdiIcons(sourcePath: string, destinationPath: string) {
    if (sourcePath == destinationPath) return
    if (
      !exts.some((ext) => sourcePath.endsWith(ext)) ||
      !dirs.some((dir) => sourcePath.includes(dir.replace('./', '/')))
    )
      return
    try {
      const sourceContent = await fs.readFile(sourcePath, 'utf-8')
      if (sourceContent.includes(pattern)) {
        try {
          const newRegex = new RegExp(`\\${pattern}[a-zA-Z0-9]+`, 'g')
          const newIcons = sourceContent.match(newRegex)
          if (newIcons && newIcons.length > 0) {
            let existingIcons: string[] = []
            try {
              const existingContent = await fs.readFile(destinationPath, 'utf-8')
              const existingRegex = new RegExp(`${pattern.replace('$', '')}[a-zA-Z0-9]+`, 'g')
              existingIcons = existingContent.match(existingRegex) || []
            } catch {
              await fs.writeFile(
                destinationPath,
                `/* eslint-disable */\n/* prettier-ignore */\n// @ts-nocheck\n// noinspection JSUnusedGlobalSymbols\n// Generated by vite-plugin-auto-import-mdi-icons\n\n` +
                  `import {} from "@mdi/js"\n\n` +
                  `export default {}\n`,
              )
            }
            const existingIconsSet = new Set(existingIcons.map((icon) => '$' + icon))
            const hasNewIcons = newIcons.some((icon) => !existingIconsSet.has(icon))
            if (!hasNewIcons) return
            const icons = [...new Set([...existingIcons.map((icon) => '$' + icon), ...newIcons])]
              .sort()
              .map((icon) => icon.replace('$', ''))
            const content =
              `/* eslint-disable */\n/* prettier-ignore */\n// @ts-nocheck\n// noinspection JSUnusedGlobalSymbols\n// Generated by vite-plugin-auto-import-mdi-icons\n\n` +
              `import {\n\t${icons.join(',\n\t')}\n} from "@mdi/js"\n\n` +
              `export default {\n\t${icons.join(',\n\t')}\n}\n`
            await fs.writeFile(destinationPath, content)
            pluginLogger(`added: ${newIcons.join(', ')}`)
          }
        } catch (error) {
          console.error(`Error writing file at ${destinationPath}:`, error)
        }
      }
    } catch (error) {
      console.error(`Error reading file at ${sourcePath}:`, error)
    }
  }

  return {
    name: 'auto-import-mdi-icons',
    configResolved(resolvedConfig) {
      config = resolvedConfig
    },
    async handleHotUpdate({ file, server }) {
      await autoImportVuetifyMdiIcons(
        file,
        server.config.root + outputPath.replace(/^\.\//, '/') + outputFile,
      )
    },
    async buildStart() {
      const iconsPath = config.root + outputPath.replace(/^\.\//, '/') + outputFile

      async function scanDirectory(dir: string): Promise<void> {
        try {
          const entries = await fs.readdir(dir, { withFileTypes: true })

          for (const entry of entries) {
            const fullPath = dir + '/' + entry.name
            if (entry.isDirectory()) {
              await scanDirectory(fullPath)
            } else if (entry.isFile()) {
              await autoImportVuetifyMdiIcons(fullPath, iconsPath)
            }
          }
        } catch (error) {
          console.error(`Error scanning directory ${dir}:`, error)
        }
      }

      try {
        for (const dir of dirs) {
          const fullDir = config.root + '/' + dir.replace(/^\.\//, '')
          await scanDirectory(fullDir)
        }
      } catch (error) {
        console.error('Error during buildStart scan:', error)
      }
    },
  }
}
  1. Add plugin to ./vite.config.ts
ts
// ...
import { AutoImportMdiIcons } from './src/plugins/icons'
// ...
export default defineConfig({
  plugins: [
// ...
    AutoImportMdiIcons({})
// ...
  ],
// ...
})
  1. Add new icon to @/pages/sandbox/index.vue and check that it is auto imported.
vue
<!-- ... -->
<v-card-title><v-icon icon="$mdiHome" /><v-icon icon="$mdiHeart" />{{ t('sandbox.title') }}</v-card-title>
<!-- ... -->