Vue.js 2 🟢 Setting Up Vue 3 Projects using Vite and Create-Vue
Vue 3 projects are scaffolded with create-vue, the official project generator, which produces a Vite-powered workspace with the options you select: TypeScript, JSX, Vue Router, Pinia, Vitest, end-to-end testing, ESLint, and Prettier. The generator replaced the older Vue CLI, which was webpack-based and has since been deprecated. Understanding the generated structure is the first step toward working productively in a Vue 3 codebase.
Vite is the build tool that underpins the project. It serves the application during development using native ES modules, which means no bundling during development and near-instant server startup. For production, it bundles with Rollup. This combination gives Vue 3 projects a development experience that is dramatically faster than the webpack-based tooling that preceded it. This chapter covers the scaffolding process, the generated structure, the Vite configuration, and the scripts that drive the development workflow.
Key point: Run npm create vue@latest to scaffold a Vue 3 project. The generator asks which features to include and produces a Vite-based workspace. Vite serves native ES modules in development and bundles with Rollup for production. The generated scripts are dev, build, preview, test, lint, and format.
Why create-vue and Vite exist
The Vue CLI problem. Vue CLI was the original scaffolding tool, built on webpack. It was powerful but slow: development server startup and rebuild times grew with the size of the project, and the configuration was complex. Vite replaced it as the recommended tool for new Vue 3 projects.
The native ES modules problem. Modern browsers support ES modules natively. Vite exploits this: during development, it serves the source files as ES modules without bundling. The browser requests each module as it is needed, and Vite transforms only the files that are requested. This makes startup and hot module replacement nearly instant regardless of project size.
The configuration problem. A Vue 3 project needs a build tool, a development server, a testing framework, and linting. Assembling these by hand is tedious and error-prone. create-vue produces a working configuration for all of them based on the options you choose, so the project is ready to develop immediately after scaffolding.
The TypeScript problem. Vue 3’s Composition API is fully typed, and <script setup lang="ts"> is the recommended way to write components. create-vue configures TypeScript, the Vue language tools, and the type checking for the build.
The testing problem. create-vue can include Vitest for unit tests and Playwright or Cypress for end-to-end tests. The testing setup is configured with the project, so tests can be written immediately.
a. Scaffolding with create-vue
The scaffolding command is run without installation:
npm create vue@latest
The generator prompts for the project name and a series of feature selections.
✔ Project name: … my-vue-app
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
Each prompt can be answered with the default by pressing Enter. The recommended answers for a modern application are Yes for TypeScript, Vue Router, Pinia, Vitest, ESLint, and Prettier. JSX and end-to-end testing depend on the project’s needs.
The generator also supports command-line flags for non-interactive scaffolding:
npm create vue@latest my-app -- --typescript --router --pinia --vitest --eslint --prettier
After scaffolding, the dependencies are installed:
cd my-vue-app
npm install
The development server is started with:
npm run dev
Vite starts in a few hundred milliseconds and prints the local URL, typically http://localhost:5173.
b. The generated project structure
The scaffolding produces a structure that separates concerns and is ready to extend.
my-vue-app/
├── public/
│ └── favicon.ico
├── src/
│ ├── assets/
│ │ ├── base.css
│ │ └── main.css
│ ├── components/
│ │ ├── HelloWorld.vue
│ │ └── icons/
│ ├── router/
│ │ └── index.ts
│ ├── stores/
│ │ └── counter.ts
│ ├── views/
│ │ ├── HomeView.vue
│ │ └── AboutView.vue
│ ├── App.vue
│ └── main.ts
├── e2e/
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
├── vitest.config.ts
├── eslint.config.ts
└── env.d.ts
| Path | Purpose |
|---|---|
index.html | Entry HTML file; Vite injects the module script |
src/main.ts | Application entry point; creates and mounts the Vue app |
src/App.vue | Root component |
src/components/ | Reusable components |
src/views/ | Route-level components |
src/router/ | Vue Router configuration |
src/stores/ | Pinia stores |
src/assets/ | Styles and static assets |
vite.config.ts | Vite configuration |
vitest.config.ts | Test configuration |
tsconfig.*.json | TypeScript configurations for different targets |
The index.html file is the application’s entry point. It contains a <div id="app"></div> and a script tag that loads src/main.ts:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>my-vue-app</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
The main.ts file creates the application and mounts it:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './assets/main.css';
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
c. The Vite configuration
The vite.config.ts file configures the development server, the production build, and the Vue plugin.
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools';
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});
The @vitejs/plugin-vue plugin compiles .vue files. The vite-plugin-vue-devtools plugin adds the Vue DevTools integration. The alias maps @ to the src directory, so imports can be written as @/components/HelloWorld.vue instead of relative paths.
The resolve.alias configuration is the most commonly extended part of the file. A project might add aliases for features, shared code, or assets:
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@features': fileURLToPath(new URL('./src/features', import.meta.url)),
'@shared': fileURLToPath(new URL('./src/shared', import.meta.url)),
},
},
d. The package.json scripts
The generated package.json includes scripts for the development workflow.
{
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"test:e2e": "playwright test",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix",
"format": "prettier --write src/"
}
}
| Script | Purpose |
|---|---|
dev | Start the development server |
build | Type-check and build for production |
preview | Serve the production build locally |
test:unit | Run unit tests with Vitest |
test:e2e | Run end-to-end tests |
type-check | Run the TypeScript compiler |
lint | Run ESLint and fix issues |
format | Format the source with Prettier |
The build script runs type-check and build-only in parallel using npm-run-all. The build-only script runs vite build, which produces the production bundle in dist/.
The preview script serves the dist/ folder locally, which is useful for verifying the production build before deploying.
e. The TypeScript configuration
The project has multiple tsconfig files, each targeting a different part of the codebase.
| File | Target |
|---|---|
tsconfig.json | Project references |
tsconfig.app.json | Application source |
tsconfig.node.json | Vite configuration and tooling |
tsconfig.vitest.json | Test files |
The tsconfig.app.json file includes the src/ directory and configures the Vue-specific compiler options:
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}
The paths configuration mirrors the Vite alias, so TypeScript resolves @/ imports correctly.
Complete Example Session
# ============================================
# PART 1: SCAFFOLD THE PROJECT
# ============================================
npm create vue@latest my-vue-app
# Answer the prompts: TypeScript, Router, Pinia, Vitest, ESLint, Prettier
# ============================================
# PART 2: INSTALL DEPENDENCIES
# ============================================
cd my-vue-app
npm install
# ============================================
# PART 3: START THE DEVELOPMENT SERVER
# ============================================
npm run dev
# Vite starts at http://localhost:5173
# ============================================
# PART 4: EXAMINE THE PROJECT STRUCTURE
# ============================================
ls -la
ls -la src/
<!-- ============================================
PART 5: EXAMINE INDEX.HTML
============================================ -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>my-vue-app</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
// ============================================
// PART 6: EXAMINE MAIN.TS
// ============================================
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './assets/main.css';
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
// ============================================
// PART 7: EXAMINE VITE CONFIG
// ============================================
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});
// ============================================
// PART 8: EXAMINE PACKAGE.JSON SCRIPTS
// ============================================
{
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"lint": "eslint . --fix",
"format": "prettier --write src/"
}
}
# ============================================
# PART 9: RUN TYPE CHECK AND BUILD
# ============================================
npm run type-check
npm run build
# Output in dist/
# ============================================
# PART 10: PREVIEW THE PRODUCTION BUILD
# ============================================
npm run preview
# Serves dist/ at http://localhost:4173
These ten parts cover scaffolding, installing dependencies, starting the development server, examining the structure, the HTML entry point, the main entry file, the Vite configuration, the package scripts, type-checking and building, and previewing the production build.
Quick Reference
Scaffolding Commands
| Command | Purpose |
|---|---|
npm create vue@latest | Interactive scaffolding |
npm create vue@latest my-app | Scaffold with a name |
npm create vue@latest my-app -- --typescript --router --pinia | Non-interactive |
Project Structure
| Path | Purpose |
|---|---|
index.html | HTML entry point |
src/main.ts | Application entry |
src/App.vue | Root component |
src/components/ | Reusable components |
src/views/ | Route-level components |
src/router/ | Vue Router configuration |
src/stores/ | Pinia stores |
vite.config.ts | Vite configuration |
vitest.config.ts | Test configuration |
Package Scripts
| Script | Command | Purpose |
|---|---|---|
dev | vite | Development server |
build | run-p type-check build-only | Production build |
preview | vite preview | Serve the build |
test:unit | vitest | Unit tests |
type-check | vue-tsc --build | Type checking |
lint | eslint . --fix | Linting |
format | prettier --write src/ | Formatting |
Vite Configuration
| Option | Purpose |
|---|---|
plugins | Vue, DevTools, and other plugins |
resolve.alias | Import path aliases |
server.port | Development server port |
build.outDir | Production output directory |
build.sourcemap | Source map generation |
TypeScript Configuration
| File | Target |
|---|---|
tsconfig.json | Project references |
tsconfig.app.json | Application source |
tsconfig.node.json | Vite configuration |
tsconfig.vitest.json | Test files |
Best Practices
✅ Do This:
# Use create-vue for new projects
npm create vue@latest
# Include TypeScript, Router, Pinia, Vitest
# Answer Yes to the recommended prompts
# Use the @ alias for imports
import HelloWorld from '@/components/HelloWorld.vue';
# Run type-check before building
npm run type-check
# Preview the production build locally
npm run preview
❌ Don’t Do This:
# Use the deprecated Vue CLI
vue create my-app # ❌ use create-vue instead
# Skip TypeScript for a new project
# TypeScript is the default recommendation # ❌
# Forget to install dependencies after scaffolding
npm run dev # ❌ fails without npm install
# Commit the dist/ folder
git add dist/ # ❌ build artifacts are regenerated
# Edit node_modules or lock files manually
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
npm run dev fails | Dependencies not installed | Run npm install |
| Import path not resolved | Alias not configured | Add to vite.config.ts and tsconfig |
| Type errors in IDE | Vue language tools not installed | Install the Volar extension |
| Build fails on type errors | vue-tsc runs before vite build | Fix the type errors |
| Slow HMR | Full reload instead of hot replacement | Check the component’s state |
| Port conflict | Another process on 5173 | Change server.port in Vite config |
| Assets not found in build | Wrong path or missing public/ | Use import or /public paths |
Real-World Examples
1. Scaffold with Options
npm create vue@latest my-app -- --typescript --router --pinia --vitest
2. Install and Run
cd my-app && npm install && npm run dev
3. Add an Alias
resolve: {
alias: {
'@features': fileURLToPath(new URL('./src/features', import.meta.url)),
},
}
4. Create a Component
<script setup lang="ts">
defineProps<{ msg: string }>();
</script>
<template>
<p>{{ msg }}</p>
</template>
5. Add a Route
{
path: '/about',
name: 'about',
component: () => import('@/views/AboutView.vue'),
}
6. Add a Store
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
return { count };
});
7. Run Tests
npm run test:unit
8. Type Check
npm run type-check
9. Build for Production
npm run build
10. Preview the Build
npm run preview
Visual
Scaffolding Flow
┌──────────────────────────────────────────────────────────────┐
│ npm create vue@latest │
│ │ │
│ ▼ │
│ Interactive prompts │
│ ├── TypeScript? │
│ ├── JSX? │
│ ├── Vue Router? │
│ ├── Pinia? │
│ ├── Vitest? │
│ ├── End-to-end? │
│ ├── ESLint? │
│ └── Prettier? │
│ │ │
│ ▼ │
│ Project generated │
│ │ │
│ ▼ │
│ npm install │
│ │ │
│ ▼ │
│ npm run dev │
└──────────────────────────────────────────────────────────────┘
Project Structure
┌──────────────────────────────────────────────────────────────┐
│ my-vue-app/ │
│ ├── index.html ← entry point │
│ ├── src/ │
│ │ ├── main.ts ← creates and mounts app │
│ │ ├── App.vue ← root component │
│ │ ├── components/ ← reusable components │
│ │ ├── views/ ← route-level components │
│ │ ├── router/ ← Vue Router config │
│ │ ├── stores/ ← Pinia stores │
│ │ └── assets/ ← styles and static files │
│ ├── vite.config.ts ← build configuration │
│ └── package.json ← scripts and dependencies │
└──────────────────────────────────────────────────────────────┘
Vite Development vs Production
┌──────────────────────────────────────────────────────────────┐
│ DEVELOPMENT: │
│ Vite server → native ES modules → browser │
│ No bundling. Instant startup. HMR on change. │
│ │
│ PRODUCTION: │
│ vite build → Rollup → bundled, minified output in dist/ │
│ │
│ vite preview → serves dist/ locally for verification │
└──────────────────────────────────────────────────────────────┘
Scripts Flow
┌──────────────────────────────────────────────────────────────┐
│ npm run dev → development server │
│ npm run type-check → vue-tsc │
│ npm run build → type-check + vite build │
│ npm run preview → serve dist/ │
│ npm run test:unit → vitest │
│ npm run lint → eslint │
│ npm run format → prettier │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Scaffolding tool | create-vue |
| Command | npm create vue@latest |
| Build tool | Vite |
| Dev server | Native ES modules, no bundling |
| Production build | Rollup via vite build |
| Default port | 5173 |
| Preview port | 4173 |
| Alias | @ maps to src/ |
| TypeScript | Configured with vue-tsc |
| Testing | Vitest for unit tests |
| Linting | ESLint |
| Formatting | Prettier |
Key takeaways:
- Use
create-vueto scaffold Vue 3 projects. It replaces the deprecated Vue CLI and produces a Vite-based workspace with the features you select. - Vite serves native ES modules in development. No bundling during development means near-instant server startup and hot module replacement regardless of project size.
- The generated structure separates concerns. Components, views, router, stores, and assets each have their own folder. The
main.tsfile creates the app and registers the plugins. - The
@alias maps tosrc/. Configured in bothvite.config.tsandtsconfig.app.json, it allows imports like@/components/HelloWorld.vueinstead of relative paths. - The scripts cover the full workflow.
dev,build,preview,test:unit,type-check,lint, andformatare all preconfigured. - TypeScript is checked with
vue-tsc. Thetype-checkscript runs the Vue-aware TypeScript compiler, and thebuildscript runs it before bundling. - Preview the production build locally.
npm run previewserves thedist/folder, which verifies the build before deployment.
Remember: Setting up a Vue 3 project is a single command, and the generated workspace is ready to develop immediately. Vite is the reason the development experience is fast: it serves native ES modules without bundling, and it only transforms the files the browser requests. The generated structure gives each concern its own folder, and the configuration handles TypeScript, routing, state, testing, linting, and formatting. Understanding the structure and the scripts is the foundation for working productively in any Vue 3 codebase, and the scaffolding tool makes the setup reproducible across projects and teams.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!