VueJS 3 tutorial – Create your first Vue application
verified on 7 September 2026 · 5 min
Create the project with npm create vue@latest, answer “no” to TypeScript, then run npm install and npm run dev. A Vue application comes out of createApp(App).mount('#app'), a value declared with ref() is displayed in the template between double curly braces and updates on its own.
A Vue application is an instance created by createApp and attached to an element on the page. This chapter sets the project up, then displays a first piece of data on screen.
Creating the project
Vue ships an official generator, create-vue, which sets up a Vite project configured for Vue. Open a terminal in the folder where you keep your projects:
npm create vue@latestThe generator asks a series of questions. For this tutorial, answer:
- Project name:
boutique-vue - TypeScript: No, we are staying in JavaScript so as not to learn two things at once.
- JSX, Vue Router, Pinia, ESLint, Prettier: No. We will add them when they become useful.
- Vitest: Yes if you want to write tests, otherwise No.
Plenty of tutorials tell you to run npm create vue@latest -- --default to “save time”. In create-vue 3.23, that flag turns TypeScript on: you end up with a src/main.ts and tsconfig files, which is not what you want here. Answer the questions instead.
Install the dependencies, then start the development server:
On Node 22, the bundled npm (10.9) stops with npm error Cannot read properties of null (reading 'edgesOut'), on a fresh project as well as on the code of this tutorial. Update npm before installing: npm install -g npm@latest. Node 24 does not have this problem.
cd boutique-vue
npm install
npm run devVite prints an address, usually http://localhost:5173/. Open it: the project home page appears.
What the generator created
boutique-vue/
├── index.html ← la seule vraie page HTML du site
├── package.json
├── vite.config.js
├── public/ ← fichiers servis tels quels
└── src/
├── main.js ← point d’entrée : crée et monte l’application
├── App.vue ← composant racine
├── assets/
└── components/Three files matter for now. The generator also creates src/assets/main.css and base.css, the style of its home page: you can delete them, along with the import './assets/main.css' line that opens src/main.js, once the tutorial’s CSS replaces them at the end of the chapter.
index.html holds an empty container and loads the entry point. That is all: the rest of the page will be produced by Vue.
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>src/main.js creates the application from the root component and mounts it in that container.
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')createApp(App) builds the instance, .mount('#app') gives it the DOM element it is allowed to manage. From then on, everything inside <div id="app"> belongs to Vue.
The first component
A .vue file is a single-file component: the script, the template and the styles for one piece of interface all live in the same place. Empty src/App.vue and write:
<script setup>
import { ref } from 'vue'
const product = ref('T-Shirt')
</script>
<template>
<div class="product-display">
<h1>{{ product }}</h1>
</div>
</template>Save: the browser shows “T-Shirt” without a reload. That is Vite’s hot module replacement.
This file will keep growing until the chapter on components, where we will split it up.
Understanding those seven lines
<script setup> tells Vue that this block describes the component logic. Everything you declare at its top level is usable in the template, with nothing to export.
ref('T-Shirt') creates a reactive reference: a box that holds a value and warns Vue when it changes. In the script, you open the box with .value:
const product = ref('T-Shirt')
console.log(product.value) // 'T-Shirt'
product.value = 'Pull' // the heading changes on screen immediatelyIn the template, on the other hand, you write {{ product }} without .value: Vue unwraps the reference for you.
The double curly braces are an interpolation. They accept any JavaScript expression, not just a variable name:
<h1>{{ product }}</h1>
<h1>{{ product.toUpperCase() }}</h1>
<h1>{{ 'Notre ' + product }}</h1>Reactivity, in practice
Change the initial value to 'Pull' and save: the heading follows. You have not written a single line to look up the h1 and change its content. That is the heart of Vue: you describe what the page should display for a given state, and Vue handles the update.
The same component would be written export default { data() { return { product: 'T-Shirt' } } }, and the script would reach it through this.product. With <script setup>, this does not exist: there are only variables.
What about the other build tools?
create-vue installs Vite, the tool the Vue team recommends. Other bundlers compile .vue files too: Parcel through @parcel/transformer-vue, or webpack with vue-loader.
They work, but they are slower and off the beaten track. Measured on 2 September 2026 on the same single-component application:
Vite 8.2.2 build en 1,05 s → 60,57 kB (23,91 kB gzip)
Parcel 2.16.4 build en 10,57 s → 92,77 kBThe output of npm run build is a folder of static files. What it contains decides your search visibility, a subject covered separately.
Stick with Vite for a new project. The alternatives are only worth it if your existing build chain already depends on them.
The CSS used in this tutorial
To make the application look like something, create src/assets/boutique.css with the handful of rules used throughout the chapters:
.product-display { display: flex; gap: 32px; padding: 24px; font-family: system-ui, sans-serif; }
.product-image img { width: 240px; }
.variants-wrapper { display: flex; gap: 10px; }
.color-circle { width: 50px; height: 50px; margin-top: 8px; border: 2px solid #d8d8d8; border-radius: 50%; cursor: pointer; }
.button { border: none; background: #1d4ed8; color: #fff; padding: 10px 18px; border-radius: 6px; cursor: pointer; }
.disabledButton { background-color: #d8d8d8; cursor: not-allowed; }Then import it in the entry point:
import './assets/boutique.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')brand value set to “Gekkode” and display it in the heading, before the product name. Then change its value in the code and check that the page updates without a reload.Common errors
--default flag turns TypeScript on: you end up with a src/main.ts and tsconfig files. Run the command without the flag and answer “no” to the TypeScript question.ref() returns an object. In the script, write product.value, in the template {{ product }} is enough, Vue unwraps the reference.node -v before creating the project.