Chapter 1 of 11

VueJS 3 tutorial – Create your first Vue application

verified on 7 September 2026 · 5 min

Quick answer

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.

By the end of this chapter, you will have a Vue 3 project up and running, and you will know how to display a reactive value on screen.

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:

bash
npm create vue@latest

The 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.
Careful with the –default option

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:

npm install fails with the npm bundled with Node 22

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.

bash
cd boutique-vue
npm install
npm run dev

Vite prints an address, usually http://localhost:5173/. Open it: the project home page appears.

What the generator created

bash
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.

index.html
<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.

src/main.js
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:

src/App.vue
<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:

javascript
const product = ref('T-Shirt')

console.log(product.value)   // 'T-Shirt'
product.value = 'Pull'       // the heading changes on screen immediately

In 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:

html
<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.

In the Options API

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:

bash
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 kB

The 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:

src/assets/boutique.css
.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:

src/main.js
import './assets/boutique.css'
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
ExerciseAdd a 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

npm create vue@latest -- --default installs TypeScript In create-vue 3.23, the --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.
Forgetting .value in the script ref() returns an object. In the script, write product.value, in the template {{ product }} is enough, Vue unwraps the reference.
A Node version that is too old create-vue requires Node 22.18 or later. Check with node -v before creating the project.
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.