99

Concurrent visitors over the last 30 minutes. (~)

Peak concurrent visitors -
Yesterday: 0 / Today: 0

Starting a Vue Project with Vite

Starting a Vue Project with Vite

This post explains how to start a Vue project with the Vite.js build tool.
The configuration for JavaScript and TypeScript projects is explained separately.

You need Node.js 20.19 or later installed.
If you use version 22.x, you need 22.13 or later.

# Creating a basic project

Open the project folder in VS Code and run the following commands in the terminal in order.

BASH
1
2
3
4
5
6
# Set up the project in the current path npm create vite@latest . Select a framework: Vue Select a variant: TypeScript or JavaScript Install with npm and start now? Yes

Or, in the terminal, move to the path where you want to create the project and run the following commands in order.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Create and set up a project folder in the current path npm create vite@latest <project_folder_name> Select a framework: Vue Select a variant: TypeScript or JavaScript Install with npm and start now? No # Move to the project path cd <project_folder_name> # Install dependency packages npm i # Open the current path in a new VS Code window code . # Or open it in the current VS Code window code . -r # Or open the project manually # Run the development server npm run dev

You can install and then use the code command as follows.
VS Code > Command Palette (Ctrl(Cmd) + Shift + P) > search for code > select Install 'code' command in PATH
Install 'code' command in PATH

# Configuring ESLint + Prettier

  • ESLint: Checks code quality and detects bugs and anti-patterns
  • Prettier - Code formatter: Manages code style and formatting, and lets you apply a consistent code style

# Installing VS Code extensions

If you have already installed the extensions, skip this step!

To use ESLint and Prettier, install each extension in VS Code.
It is a good idea to restart VS Code after installing them.

ESLint Prettier - Code formatter

Because this is a Vue project, you can also install the following extension for extra help.

  • Vue - Official: Supports syntax highlighting, autocompletion, error checking, and more for Vue projects

Vue - Official

# Installing and configuring packages

Install each dependency package so you can use it in the project.
None of these packages are needed at runtime because they are all for development, so install them as 'dev dependencies' with the -D flag.

BASH
1
npm i -D eslint @eslint/js globals eslint-plugin-vue prettier eslint-config-prettier eslint-plugin-prettier
For JavaScript.
BASH
1
npm i -D eslint @eslint/js eslint-plugin-vue @vue/eslint-config-typescript prettier eslint-config-prettier eslint-plugin-prettier
For TypeScript.
Package Description
eslint ESLint core package / Checks code quality and detects bugs and anti-patterns
@eslint/js ESLint's default set of recommended rules
globals A list of global variables for runtime environments such as the browser
eslint-plugin-vue Vue support plugin, supports syntax analysis and linting
@vue/eslint-config-typescript Connects TypeScript rules along with a parser that analyzes the inside of *.vue files
prettier Prettier core package / Manages code style and formatting, and lets you apply a consistent code style
eslint-config-prettier Turns off ESLint rules that conflict with Prettier, mostly formatting rules that eslint-plugin-vue turns on
eslint-plugin-prettier Integrates Prettier rules into ESLint rules

When the installation is complete, create an eslint.config.js file in the project root and add the following content.

/eslint.config.js
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import js from '@eslint/js' import globals from 'globals' import pluginVue from 'eslint-plugin-vue' import prettierRecommended from 'eslint-plugin-prettier/recommended' import { defineConfig, globalIgnores } from 'eslint/config' export default defineConfig([ { files: ['**/*.{vue,js,mjs,jsx}'] }, globalIgnores(['**/dist/**']), { languageOptions: { globals: globals.browser } }, js.configs.recommended, pluginVue.configs['flat/recommended'], prettierRecommended ])
For JavaScript.
/eslint.config.js
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import js from '@eslint/js' import pluginVue from 'eslint-plugin-vue' import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' import prettierRecommended from 'eslint-plugin-prettier/recommended' import { globalIgnores } from 'eslint/config' export default defineConfigWithVueTs( { files: ['**/*.{vue,ts,mts,tsx}'] }, globalIgnores(['**/dist/**']), js.configs.recommended, pluginVue.configs['flat/recommended'], vueTsConfigs.recommended, prettierRecommended )
For TypeScript.

Starting with ESLint 9, the flat config that uses eslint.config.js became the default, and version 10 removed support for the old .eslintrc.* format.
In TypeScript, you use defineConfigWithVueTs because it also connects the parser that analyzes the inside of <script lang="ts"> in *.vue files.
If you list typescript-eslint directly, the parser is not connected and a Parsing error occurs.
vueTsConfigs.recommended includes only TypeScript rules, so if you also want ESLint's default recommended rules such as no-empty, add js.configs.recommended as well.

If needed, you can override the rules used in <template>, <script>, and so on in *.vue files with custom rules.
Just add the following object at the very end of the configuration.
You can find the full list of rules in ESLint plugin for Vue.js / Rules.

/eslint.config.js
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ... export default defineConfigWithVueTs( // ... { rules: { 'vue/html-closing-bracket-newline': ['error', { singleline: 'never', multiline: 'never' }], 'vue/html-self-closing': ['error', { html: { void: 'always', normal: 'never', component: 'always' }, svg: 'always', math: 'always' }], 'vue/comment-directive': 'off', 'vue/no-v-html': 'off' } } )
Adding custom Vue rules.

Also, create a .prettierrc file in the project root and add the following content.
You can find the full list of options in Prettier / Options.

/.prettierrc
JSON
1
2
3
4
5
6
7
8
9
10
{ "semi": false, "singleQuote": true, "singleAttributePerLine": true, "bracketSameLine": true, "endOfLine": "auto", "trailingComma": "none", "arrowParens": "avoid", "printWidth": 100 }

# Setting up automatic formatting

You can use automatic formatting through user settings (local) that apply only to the current project.
Create a .vscode/settings.json folder and file in the project root, and add the following content.

/.vscode/settings.json
JSON
1
2
3
4
{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode" }

This applies not only to *.vue, *.js, and *.ts files but to every file Prettier supports, such as *.json, *.css, *.html, and *.md.
If you want a specific language to behave differently, you can override it with language-specific settings such as "[vue]": { ... }.

You can open the user settings (global) by searching for settings.json in the Command Palette.

Preferences: Open User Settings (JSON)

# Configuring path aliases

Path aliases are convenient because they make it easy to reference files in your project.

/vite.config.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vite.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: [ { find: '@', replacement: '/src' }, { find: 'node_modules', replacement: '/node_modules' } ] } })
/src/components/a/b/c/MyComponent.vue
VUE
1
2
3
4
5
6
7
8
9
<script setup lang="ts"> // import { useMovieStore } from '../../../../store/movie' import { useMovieStore } from '@/store/movie' </script> <style> @import 'node_modules/swiper/swiper.css'; @import 'node_modules/swiper/modules/navigation.css'; </style>
Example of using path aliases

So that TypeScript can also recognize the paths, add the following configuration options.
The tsconfig.json file that Vite creates only references other configuration files, so add the options to the tsconfig.app.json file.

/tsconfig.app.json
JSON
1
2
3
4
5
6
7
8
9
{ "compilerOptions": { // ... "paths": { "@/*": ["./src/*"], "node_modules/*": ["./node_modules/*"] } } }
TypeScript v6

If you use TypeScript 5 or earlier, you may need the baseUrl option.

/tsconfig.app.json
JSON
1
2
3
4
5
6
7
8
9
10
{ "compilerOptions": { // ... "baseUrl": "./", "paths": { "@/*": ["./src/*"], "node_modules/*": ["./node_modules/*"] } } }
TypeScript v5

# Official Vue Starter

Instead of doing all of the configuration so far yourself, you can also get it in one go with the official Vue starter.
Unlike Vite's Vue template, which provides only a minimal setup, it lets you choose and set up a router, state management, testing, a linter, and a formatter together.
It also includes the @ path alias, .editorconfig, and .vscode/settings.json.

The command to create the project is the same. Just select Official Vue Starter ↗ at Select a variant:.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Create and set up a project folder in the current path npm create vite@latest <project_folder_name> Select a framework: Vue Select a variant: Official Vue Starter ↗ Use TypeScript? Yes or No Select features to include in your project: JSX Support Router (SPA development) Pinia (state management) Vitest (unit testing) End-to-End Testing Linter (error prevention) Prettier (code formatting)

When you select Official Vue Starter ↗, Vite hands things over to the official starter, so the questions that follow come from the official starter.
Because you have already entered the project folder name, it does not ask for the name again.
For Select features to include in your project:, move with the arrow keys and select with the Space key.

When you finish selecting, only the project folder is created, so install the dependencies and run the development server yourself.

BASH
1
2
3
cd <project_folder_name> npm i npm run dev

If you select Linter (error prevention), eslint.config.ts and .oxlintrc.json are created together, and the following scripts are added to package.json.

/package.json
JSON
1
2
3
4
5
6
7
8
{ "scripts": { "lint": "run-s \"lint:*\"", "lint:oxlint": "oxlint . --fix", "lint:eslint": "eslint . --fix --cache", "format": "prettier --write --experimental-cli src/" } }

Also, add rules to the .prettierrc file as follows.

JSON
1
2
3
4
5
6
7
8
9
10
11
{ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, "singleAttributePerLine": true, "bracketSameLine": true, "endOfLine": "auto", "trailingComma": "none", "arrowParens": "avoid", "printWidth": 100 }

# ESLint and oxlint

oxlint is a linter written in Rust, and it runs faster than ESLint.
However, it cannot replace rules that use type information or the Vue-specific rules of eslint-plugin-vue, so it is used together with ESLint.
npm run lint runs oxlint first and then runs ESLint.

By default, the two linters check overlapping things, so if you leave them as they are, the same problem gets reported twice.
The buildFromOxlintConfigFile call in eslint.config.ts is what solves this.

/eslint.config.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { globalIgnores } from 'eslint/config' import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' import pluginVue from 'eslint-plugin-vue' import pluginOxlint from 'eslint-plugin-oxlint' import skipFormatting from 'eslint-config-prettier/flat' export default defineConfigWithVueTs( { files: ['**/*.{vue,ts,mts,tsx}'] }, globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), ...pluginVue.configs['flat/essential'], vueTsConfigs.recommended, ...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'), skipFormatting )

It reads .oxlintrc.json and turns off the ESLint rules that oxlint already checks.
This removes the duplicate reports and also reduces how much ESLint has to check.

# Matching linter versions

eslint-plugin-oxlint and oxlint must be pinned to the same version, but because of a problem in the template configuration, their versions may differ.
In that case, an error like the following may appear while you install packages with npm i.

BASH
1
2
3
4
npm error code ERESOLVE npm error Found: oxlint@1.74.0 npm error Could not resolve dependency: npm error peer oxlint@"~1.73.0" from eslint-plugin-oxlint@1.73.0

Then you need to change the two packages to the same version, and when you match them, change to the lower version.

/package.json
JSON
1
2
3
4
5
6
{ "devDependencies": { "eslint-plugin-oxlint": "~1.73.0", "oxlint": "~1.73.0" } }
Matching the versions of the two packages

After the change, run npm i again and the installation completes.

# File nesting

The official starter turns on file nesting in .vscode/settings.json.
It is a feature that shows related files as children of a main file in the VS Code Explorer.

File nesting

/.vscode/settings.json
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
{ "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "tsconfig.json": "tsconfig.*.json, env.d.ts, typed-router.d.ts", "vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*", "package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .oxfmt*, .prettier*, prettier*, .editorconfig, bun.lock, nub.lock" }, "editor.codeActionsOnSave": { "source.fixAll": "explicit" }, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode" }

If you find it inconvenient, you can turn it off by changing the option to false.
If you set it explicitly to false instead of removing the option, this project never uses file nesting, regardless of your global settings.

/.vscode/settings.json
JSON
1
2
3
{ "explorer.fileNesting.enabled": false }

# Configuring SCSS

Vite supports SCSS (Sass) out of the box, so you can use it without any extra configuration once you install a compiler package.

BASH
1
npm i -D sass-embedded

The compiler comes in two packages: sass and sass-embedded.
If sass-embedded is installed, Vite uses sass-embedded. Otherwise, it uses sass.

Package Description
sass A package that converts the compiler written in Dart into JavaScript
sass-embedded A package that uses a native Dart executable, and compiles faster in most situations

When the installation is complete, you can use it by adding the lang="scss" attribute to the <style> element in *.vue files.

/src/components/MyComponent.vue
VUE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template> <div class="card"> <h2 class="title">Title</h2> </div> </template> <style lang="scss" scoped> .card { padding: 20px; .title { color: #2f6fed; } } </style>

You can also change the extension of the global style file created with the project to .scss.
If you change the extension, also update the import path in the entry file.

/src/main.ts
TS
1
2
3
4
5
6
import { createApp } from 'vue' import './style.css' import './style.scss' import App from './App.vue' createApp(App).mount('#app')

To use variables or mixins in several components, you have to import them with @use in each file every time.
With Vite's css.preprocessorOptions option, you can automatically add the code you want at the very top of every SCSS file.

First, create a file for your variables and mixins.
If a file name starts with an underscore (_), it becomes a partial that is not compiled on its own and is used only when other files import it.

/src/assets/scss/_variables.scss
SCSS
1
2
3
4
5
6
7
8
$color-primary: #2f6fed; $breakpoint-tablet: 768px; @mixin flex-center { display: flex; align-items: center; justify-content: center; }

Then add the additionalData option to the Vite configuration.
You can use the @ path alias you configured earlier as is.

/vite.config.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vite.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: [ { find: '@', replacement: '/src' }, { find: 'node_modules', replacement: '/node_modules' } ] }, css: { preprocessorOptions: { scss: { additionalData: '@use "@/assets/scss/variables" as *;' } } } })

You can omit the underscore and the extension from the name of the file you import.
as * imports without a namespace, so you can use $color-primary instead of variables.$color-primary.

Now you can use the variables and mixins in every component without @use.

/src/components/MyComponent.vue
VUE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style lang="scss" scoped> .card { @include flex-center; padding: 20px; .title { color: $color-primary; } } @media (min-width: $breakpoint-tablet) { .card { padding: 40px; } } </style>

Write only definitions such as variables, mixins, and functions in the file you import with additionalData.
Vite compiles each <style> element and each SCSS file separately, so be careful: if this file contains actual CSS rules, they are duplicated in every compiled result!

Sass's @import rule has been deprecated since Dart Sass 1.80 and will be removed in the future.
In new code, use @use.
For more on SCSS syntax, see The Complete Guide to SCSS/Sass.