| |

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
PathPurpose
index.htmlEntry HTML file; Vite injects the module script
src/main.tsApplication entry point; creates and mounts the Vue app
src/App.vueRoot 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.tsVite configuration
vitest.config.tsTest configuration
tsconfig.*.jsonTypeScript 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/"
  }
}
ScriptPurpose
devStart the development server
buildType-check and build for production
previewServe the production build locally
test:unitRun unit tests with Vitest
test:e2eRun end-to-end tests
type-checkRun the TypeScript compiler
lintRun ESLint and fix issues
formatFormat 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.

FileTarget
tsconfig.jsonProject references
tsconfig.app.jsonApplication source
tsconfig.node.jsonVite configuration and tooling
tsconfig.vitest.jsonTest 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

CommandPurpose
npm create vue@latestInteractive scaffolding
npm create vue@latest my-appScaffold with a name
npm create vue@latest my-app -- --typescript --router --piniaNon-interactive

Project Structure

PathPurpose
index.htmlHTML entry point
src/main.tsApplication entry
src/App.vueRoot component
src/components/Reusable components
src/views/Route-level components
src/router/Vue Router configuration
src/stores/Pinia stores
vite.config.tsVite configuration
vitest.config.tsTest configuration

Package Scripts

ScriptCommandPurpose
devviteDevelopment server
buildrun-p type-check build-onlyProduction build
previewvite previewServe the build
test:unitvitestUnit tests
type-checkvue-tsc --buildType checking
linteslint . --fixLinting
formatprettier --write src/Formatting

Vite Configuration

OptionPurpose
pluginsVue, DevTools, and other plugins
resolve.aliasImport path aliases
server.portDevelopment server port
build.outDirProduction output directory
build.sourcemapSource map generation

TypeScript Configuration

FileTarget
tsconfig.jsonProject references
tsconfig.app.jsonApplication source
tsconfig.node.jsonVite configuration
tsconfig.vitest.jsonTest 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

PitfallWhy It HappensFix
npm run dev failsDependencies not installedRun npm install
Import path not resolvedAlias not configuredAdd to vite.config.ts and tsconfig
Type errors in IDEVue language tools not installedInstall the Volar extension
Build fails on type errorsvue-tsc runs before vite buildFix the type errors
Slow HMRFull reload instead of hot replacementCheck the component’s state
Port conflictAnother process on 5173Change server.port in Vite config
Assets not found in buildWrong 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

ItemValue
Scaffolding toolcreate-vue
Commandnpm create vue@latest
Build toolVite
Dev serverNative ES modules, no bundling
Production buildRollup via vite build
Default port5173
Preview port4173
Alias@ maps to src/
TypeScriptConfigured with vue-tsc
TestingVitest for unit tests
LintingESLint
FormattingPrettier

Key takeaways:

  • Use create-vue to 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.ts file creates the app and registers the plugins.
  • The @ alias maps to src/. Configured in both vite.config.ts and tsconfig.app.json, it allows imports like @/components/HelloWorld.vue instead of relative paths.
  • The scripts cover the full workflow. dev, build, preview, test:unit, type-check, lint, and format are all preconfigured.
  • TypeScript is checked with vue-tsc. The type-check script runs the Vue-aware TypeScript compiler, and the build script runs it before bundling.
  • Preview the production build locally. npm run preview serves the dist/ 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!