Start a project with create-vue
Scaffold a Vue project with create-vue on Node 22 or 24, follow index.html to main.ts to the mounted app, and know what the dev, build and preview scripts each do.
What you'll learn
When you finish, you can do these.- 1Create a Vue project with create-vue on Node 22 or 24
- 2Explain how index.html and main.ts start the app
- 3Import from src/ in any folder with the @/ alias
- 4Run dev while you work, then build and preview the app
The lessons on this site run your code in a playground, an editor and a preview inside the browser. It quietly does three jobs: it serves a page, turns your Vue code into JavaScript the browser can run, and refreshes the preview while you type. An app of your own needs those jobs done on your computer, plus files to upload to a host, the service that puts a site online.
create-vue, the official project generator, sets up such an app in one command. Vite, the build tool it installs, does the jobs and makes the files. The Code tab holds the commands and the new project's main files. You need one idea from the lesson Your first component, in the Beginner track: a component is one .vue file that holds its own HTML, JavaScript and CSS.
Scaffold a project
To scaffold a project is to let a tool write its first files, from commands you type in a terminal.
Node.js runs JavaScript outside the browser and comes with npm, which installs packages such as Vue.
create-vue 3.24 and the projects it generates ask for Node ^22.18.0 || >=24.12.0: in practice Node 22 or 24. Node 20 stopped getting security fixes on 30 April 2026.
The Code tab's commands.sh prints your Node version with node -v, creates a project called my-app, enters its folder and installs its packages:
node -v
npm create vue@latest my-app -- --ts --router --pinia --vitest --playwright --eslint --prettier
cd my-app
npm install
When npm install ends, my-app holds a working Vue app with an example page.
Each flag answers a question the command would otherwise ask: --ts for TypeScript, --router for pages, --pinia for shared state, --vitest and --playwright for tests, --eslint and --prettier to check and format your code. With npm, the -- before the flags tells npm they belong to create-vue.
From index.html to a mounted app
A new project holds many files, and three of them decide how the app starts: index.html, src/main.ts and vite.config.ts. The Code tab's structure.txt lists the whole my-app folder.
The first, index.html, is the page Vite serves. It holds an empty element and a script tag that points at your code:
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
The browser loads the page, and the page loads src/main.ts, the second file, which builds the app.
- index.htmlloads /src/main.ts
- src/main.tsimports App.vue
- createApp(App).use(...)installs plugins
- mount('#app')renders into the div
After its imports, src/main.ts takes those steps in four lines:
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
createApp(App) makes an app whose root component, the one that holds the whole page, is App.vue. app.use() installs a plugin, an add-on for the whole app: here Pinia for shared state and the router for pages. app.mount('#app') renders App.vue into the empty element, the one whose id is app.
The third file, vite.config.ts, holds Vite's settings. Vite has plugins of its own, add-ons for the build tool: vue() turns .vue files into JavaScript, and vueDevTools() adds a DevTools panel during development.
The file also sets an alias, a short name for a folder, so that @/ means src/:
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
The example home page, src/views/HomeView.vue, imports a component from '../components/TheWelcome.vue', where ../ first climbs out of the views folder. With the alias, import TheWelcome from '@/components/TheWelcome.vue' works from any folder. tsconfig.app.json, a TypeScript settings file, repeats the alias under paths for TypeScript and your editor.
Two folders hold static files such as images. src/assets/ is for files your code imports or your HTML points to, such as <img src="@/assets/logo.svg">. Vite processes them: a file under 4 KB is written into the JavaScript, and a larger one, such as a photo, is copied to dist/assets/, in the folder you upload to a host, under a hashed name like photo-Bv5LqEYI.png. The hash changes with the file, so browsers can keep a copy for a long time and never show a stale one.
public/ is copied to dist/ untouched, which suits favicon.ico, the tab icon. Refer to such files from the root, as /favicon.ico.
Dev, build and preview
A create-vue project comes with three scripts, commands you start with npm run: one to work, one to build, one to check the build.
npm run dev starts Vite's development server on your computer, at http://localhost:5173. It does not bundle, or join, your files: Vite compiles main.ts and each file it imports when the browser asks for it.
When you save a component, hot module replacement (HMR) swaps that one file in the running page, so the page updates without a reload.
npm run build runs two scripts side by side: type-check checks your TypeScript with vue-tsc --build, and build-only runs vite build. If either fails, the build fails. The result lands in dist/: index.html, an assets/ folder of hashed JavaScript and CSS files, and a copy of public/.
npm run preview serves dist/ at http://localhost:4173, so you can click through the real build. It is a check, not a host. The Environment variables, builds and deployment lesson later in this track puts dist/ on a host.
What goes wrong
Two mistakes leave a new project's page blank.
The build looks finished, so you double-click dist/index.html to see it.
npm run build
open dist/index.html # or double-click the file
The page is blank and the browser console says Access to script at 'file:///assets/index-DQV-ruX4.js' from origin 'null' has been blocked by CORS policy. The built page asks for /assets/… from the root of your disk, and browsers refuse to run module scripts from file:// addresses. Serve the folder instead.
npm run build
npm run preview # open http://localhost:4173
The second mistake is a plugin installed below app.mount('#app') in src/main.ts, where new lines tend to go.
const app = createApp(App)
app.mount('#app')
app.use(createPinia())
app.use(router)
The page stays blank. The console shows [Vue warn]: injection "Symbol(router)" not found. and then Cannot read properties of undefined (reading 'resolve'). mount() rendered App.vue at once, its Home and About links asked for the router, and the router was installed one line too late. Install every plugin first and mount last.
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
What to remember
- create-vue 3.24 needs Node 22 or 24. On Node 20, npm only warns, so check
node -vbefore you scaffold. index.htmlloadssrc/main.ts, which creates the app, installs plugins withuse()and callsmount()last.@/meanssrc/, set invite.config.tsfor Vite and intsconfig.app.jsonfor TypeScript.- Files in
src/assets/are processed and get hashed names; files inpublic/are copied as they are. npm run devserves your source files and updates the page on save;npm run buildtype-checks and writesdist/;npm run previewservesdist/so you can test the build.
In your project
Do these in a real project. Each one says how to confirm it worked.- Scaffold with the flags from
commands.shand runnpm run dev. Insrc/App.vue, changemsg="You did it!"to another text and save. The heading changes without the page reloading, and the terminal prints[vite] (client) hmr update /src/App.vue. - Run
npm run buildand opendist/index.htmlfrom your file manager: the page is blank and the console shows the CORS error. Runnpm run previewand openhttp://localhost:4173: the app works. Listdist/assets/and check that every file name carries a hash. - In
src/views/HomeView.vue, change'../components/TheWelcome.vue'to'@/components/TheWelcome.vue'. The page renders as before. Misspell it as'@/component/TheWelcome.vue': Vite's error overlay saysFailed to resolve import "@/component/TheWelcome.vue" from "src/views/HomeView.vue". Does the file exist?Fix the spelling and the overlay closes.