Setting up a Vue project with Vite
Scaffold a Vue 3 app with create-vue, understand every generated folder, and wire aliases, the dev proxy and environment files.
Scaffold and run
# interactive: choose TypeScript, Router, Pinia, ESLint, Vitest
npm create vue@latest my-app
cd my-app
npm install
npm run dev # Vite dev server with HMR
npm run build # production bundle into dist/
npm run preview # serve the built bundle locally to check it- Vite is the build tool: in development it serves files as native ES modules, so there is no bundle step and an edit appears in milliseconds.
- Hot module replacement swaps the changed module in place. Component state survives an edit unless you changed the component's own setup signature.
create-vueonly asks questions; it does not install anything. Runnpm installyourself and read the printed next steps.- Router and Pinia are opt-in at scaffold time. Adding them later means installing the package and registering the plugin in
main.js.
| Script | What it does |
|---|---|
dev | Dev server with HMR, usually on port 5173 |
build | Type-check where configured, then emit the production bundle to dist/ |
preview | Serve dist/ so the real build is verified before deploying |
test:unit | Vitest once; add -- --watch while developing |
lint | ESLint with the Vue plugin over the project |
What was generated
my-app/
index.html entry HTML; Vite injects the module script here
vite.config.js plugins, aliases, dev server and proxy
.env shared env values, committed (never secrets)
public/ copied verbatim to the build root
src/
main.js createApp(App).use(router).use(pinia).mount('#app')
App.vue root component; usually just a RouterView
assets/ imported by components, hashed by the bundler
components/ reusable pieces that know nothing about routes
views/ one component per route
router/index.js route table
stores/ Pinia storesOnly index.html is a real entry point: Vite rewrites the module script it contains and leaves the rest of the markup to you. Files under public/ are copied as-is and referenced by absolute path; assets imported from code go through the bundler and get a content hash.
components/holds pieces that receive props and render;views/holds components a route can point at.- Name files in PascalCase (
UserCard.vue) so imports and the name shown in devtools match. - Anything only one route needs can live next to that view instead of in a shared folder.
Aliases, proxy and environment
// vite.config.js
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)) }
},
server: {
port: 5173,
proxy: {
// /api/users -> http://localhost:8080/api/users, so no CORS in development
'/api': { target: 'http://localhost:8080', changeOrigin: true }
}
}
})# .env committed, applies to every environment
VITE_APP_TITLE=Guides
# .env.local gitignored, per-machine overrides
VITE_API_BASE=/api
# only VITE_ prefixed variables reach client code; read with import.meta.env⚠️
Every
VITE_ variable is inlined into the shipped JavaScript in plain text, so treat it as public. API keys, database URLs and private tokens belong on the backend, never in a .env file the front end reads.FAQ
Vite or the Vue CLI?
The Vue CLI is in maintenance mode.
create-vue plus Vite is the current official setup: faster dev feedback, far less configuration, and the same Vue 3 you would write either way.Do I have to use TypeScript?
No. Plain JavaScript still gets runtime prop warnings, and you can add TypeScript to one file at a time later. TypeScript pays off most around props, stores and API responses, where the shape is the thing you get wrong.
Related
Directives, lists and conditional rendering Production builds, environments and deployment
Last refreshed 2026-09-18.