Quick Start
This guide will walk you through creating your first Vue 3 application from scratch. We’ll use real code patterns from the Vue.js core repository (packages/vue/examples/).
Before starting, make sure you have Node.js version 20.0.0 or higher installed, as specified in the root package.json.
Create a Vue Application
Scaffold your project
Use the official create-vue tool to set up a new project with Vite: This will prompt you with several options: ✔ Project name: my-vue-app
✔ Add TypeScript? › No / Yes
✔ Add JSX Support? › No / Yes
✔ Add Vue Router? › No / Yes
✔ Add Pinia? › No / Yes
✔ Add Vitest? › No / Yes
✔ Add ESLint? › No / Yes
✔ Add Prettier? › No / Yes
For your first project, start with the defaults (No to all) to keep things simple.
Install dependencies
Navigate to your project directory and install dependencies: cd my-vue-app
npm install
This installs [email protected] and development dependencies.
Start the development server
Run the development server: Your app will be available at http://localhost:5173.
Your First Component
Let’s create a simple counter component using the Composition API. This example is inspired by Vue’s source examples.
Create the Component
Create src/components/Counter.vue:
< script setup >
import { ref } from 'vue'
// From packages/reactivity/src/ref.ts:58-64
// ref() takes an inner value and returns a reactive mutable ref object
const count = ref ( 0 )
function increment () {
count . value ++
}
function decrement () {
count . value --
}
</ script >
< template >
< div >
< h2 > Counter: {{ count }} </ h2 >
< button @ click = " decrement " > - </ button >
< button @ click = " increment " > + </ button >
</ div >
</ template >
< style scoped >
button {
margin : 0 5 px ;
padding : 5 px 15 px ;
font-size : 16 px ;
}
</ style >
The <script setup> syntax is a compile-time sugar for using the Composition API. It’s more concise than the traditional setup() function.
Use the Component
Update src/App.vue:
< script setup >
import Counter from './components/Counter.vue'
</ script >
< template >
< div id = "app" >
< h1 > My First Vue App </ h1 >
< Counter />
</ div >
</ template >
< style >
#app {
font-family : 'Helvetica' , Arial , sans-serif ;
text-align : center ;
margin-top : 60 px ;
}
</ style >
Understanding Reactivity
Vue’s reactivity system automatically tracks dependencies and updates the DOM when reactive data changes.
Using ref()
From packages/reactivity/src/ref.ts, ref() creates a reactive reference:
import { ref } from 'vue'
// Create a reactive reference
const count = ref ( 0 )
// Access the value with .value
console . log ( count . value ) // 0
// Modify the value
count . value ++
console . log ( count . value ) // 1
In JavaScript, you must use .value to access or modify refs. In templates, refs are automatically unwrapped, so you can use {{ count }} instead of {{ count.value }}.
Using computed()
From packages/runtime-core/src/apiComputed.ts, computed values automatically recalculate when their dependencies change:
import { ref , computed } from 'vue'
const count = ref ( 0 )
const doubled = computed (() => count . value * 2 )
console . log ( doubled . value ) // 0
count . value = 5
console . log ( doubled . value ) // 10
Real-World Example: GitHub Commits Viewer
Let’s build a more complex example based on packages/vue/examples/composition/commits from the Vue source:
< script setup >
import { ref , watchEffect } from 'vue'
const API_URL = 'https://api.github.com/repos/vuejs/core/commits?per_page=3&sha='
const branches = [ 'main' , 'v2-compat' ]
const currentBranch = ref ( 'main' )
const commits = ref ( null )
const truncate = ( v ) => {
const newline = v . indexOf ( ' \n ' )
return newline > 0 ? v . slice ( 0 , newline ) : v
}
const formatDate = ( v ) => v . replace ( /T | Z/ g , ' ' )
// From packages/runtime-core/src/apiWatch.ts
// watchEffect automatically tracks reactive dependencies
watchEffect (() => {
fetch ( ` ${ API_URL }${ currentBranch . value } ` )
. then ( res => res . json ())
. then ( data => {
commits . value = data
})
})
</ script >
< template >
< div >
< h1 > Latest Vue.js Commits </ h1 >
< template v-for = " branch in branches " : key = " branch " >
< input
type = "radio"
: id = " branch "
: value = " branch "
name = "branch"
v-model = " currentBranch "
/>
< label : for = " branch " > {{ branch }} </ label >
</ template >
< p > vuejs/core@{{ currentBranch }} </ p >
< ul v-if = " commits " >
< li v-for = " { html_url , sha , author , commit } in commits " : key = " sha " >
< a : href = " html_url " target = "_blank" class = "commit" >
{{ sha . slice ( 0 , 7 ) }}
</ a >
- < span class = "message" > {{ truncate ( commit . message ) }} </ span >< br />
by
< span class = "author" >
< a : href = " author_url " target = "_blank" >
{{ commit . author . name }}
</ a >
</ span >
at < span class = "date" > {{ formatDate ( commit . author . date ) }} </ span >
</ li >
</ ul >
</ div >
</ template >
< style scoped >
a {
text-decoration : none ;
color : #f66 ;
}
li {
line-height : 1.5 em ;
margin-bottom : 20 px ;
}
.author , .date {
font-weight : bold ;
}
</ style >
This example demonstrates several Vue features: reactive data with ref(), automatic dependency tracking with watchEffect(), template directives like v-for and v-model, and computed rendering.
Template Syntax
Vue uses HTML-based template syntax. From packages/vue/src/index.ts:30-102, templates are compiled into optimized render functions.
Interpolation
< template >
<!-- Text interpolation -->
< p > {{ message }} </ p >
<!-- JavaScript expressions -->
< p > {{ count + 1 }} </ p >
< p > {{ ok ? 'YES' : 'NO' }} </ p >
</ template >
Directives
< template >
<!-- Attribute binding -->
< img : src = " imageUrl " : alt = " imageAlt " />
<!-- Event handling -->
< button @ click = " handleClick " > Click me </ button >
<!-- Two-way binding -->
< input v-model = " text " />
<!-- Conditional rendering -->
< p v-if = " isVisible " > Visible content </ p >
< p v-else > Hidden content </ p >
<!-- List rendering -->
< ul >
< li v-for = " item in items " : key = " item . id " >
{{ item . name }}
</ li >
</ ul >
</ template >
Composition API Patterns
The Composition API (from packages/runtime-core/src/apiDefineComponent.ts) provides flexible ways to organize component logic.
Setup Function
From packages/vue/examples/composition/commits:44-66, the setup() function is the entry point:
import { createApp , ref , watchEffect } from 'vue'
createApp ({
setup () {
const currentBranch = ref ( 'main' )
const commits = ref ( null )
watchEffect (() => {
fetch ( ` ${ API_URL }${ currentBranch . value } ` )
. then ( res => res . json ())
. then ( data => {
commits . value = data
})
})
return {
branches: [ 'main' , 'v2-compat' ],
currentBranch ,
commits ,
truncate ,
formatDate
}
}
}). mount ( '#demo' )
Script Setup Syntax
The <script setup> syntax is simpler and more concise:
< script setup >
import { ref , watchEffect } from 'vue'
// Everything declared here is automatically exposed to the template
const currentBranch = ref ( 'main' )
const commits = ref ( null )
watchEffect (() => {
// Fetch logic here
})
</ script >
Both syntaxes are equivalent. <script setup> is compile-time sugar that eliminates the need for an explicit return statement.
CDN Quick Start (No Build Step)
For simple use cases or learning, you can use Vue from a CDN. This example is from packages/vue/examples/composition/markdown:
<! DOCTYPE html >
< html >
< head >
< title > Vue Quick Start </ title >
< script src = "https://unpkg.com/vue@3/dist/vue.global.js" ></ script >
</ head >
< body >
< div id = "app" >
< h1 > {{ message }} </ h1 >
< button @click = "count++" > Count: {{ count }} </ button >
</ div >
< script >
const { createApp , ref } = Vue
createApp ({
setup () {
const message = ref ( 'Hello Vue!' )
const count = ref ( 0 )
return {
message ,
count
}
}
}). mount ( '#app' )
</ script >
</ body >
</ html >
CDN builds are great for prototyping but not recommended for production. Use a build tool for production applications to enable:
Single-File Components (.vue files)
Tree-shaking and optimization
Hot Module Replacement
Pre-compiled templates
Build for Production
Build your application
This creates optimized production files in the dist/ directory.
Preview the production build
Test your production build locally before deploying.
Deploy
Upload the dist/ folder to your hosting provider.
Key Takeaways
Reactive Data Use ref() for primitive values and reactive() for objects. Access refs with .value in JavaScript.
Composition API Use <script setup> for cleaner, more concise component code with automatic template exposure.
Template Syntax Vue’s declarative templates use directives like v-if, v-for, v-model, and @click for common tasks.
Computed Values Use computed() for derived state that automatically updates when dependencies change.
Next Steps
Now that you’ve built your first Vue application, explore these topics:
Components : Learn about props, events, and component communication
Routing : Add navigation with Vue Router
State Management : Manage complex state with Pinia
TypeScript : Add type safety to your Vue applications
Testing : Write unit tests with Vitest
All examples in this guide are based on real code from the Vue.js core repository at packages/vue/examples/composition/. You can find more examples there!