Day 1 of 5
⏱ ~60 minutes
Vue.js in 5 Days — Day 1

Vue Components and the Composition API

You'll set up a Vue 3 project with Vite, write your first single-file component, and understand how Vue's template syntax and two-way binding work. By the end you'll have a working app running locally.

Why Vue 3 and Not Vue 2?

Vue 2 is in maintenance mode — no new features, just security fixes until end of 2023 (already passed). Vue 3 with the Composition API is the current standard. If you're starting fresh, start with Vue 3. Everything in this course is Vue 3.

The Composition API replaced the Options API as the recommended way to write components. It's more flexible, easier to test, and makes code reuse straightforward. We'll use <script setup> syntax throughout — it's the cleanest way to write Vue components today.

Setting Up Your Project

Vite is the official build tool for Vue 3. It starts in under a second and hot-reloads instantly. Don't use Vue CLI for new projects — it's legacy.

Terminal
# Create a new Vue 3 project
npm create vite@latest my-app -- --template vue

cd my-app
npm install
npm run dev

Open http://localhost:5173. You'll see the Vite + Vue welcome page. Now open the project in your editor and look at the file structure:

Project Structure
my-app/
├── src/
│   ├── App.vue          ← root component
│   ├── main.js          ← app entry point
│   ├── components/      ← your components go here
│   └── assets/          ← images, css
├── index.html
└── vite.config.js

Your First Component

A Vue single-file component (SFC) has three sections: <script setup> for logic, <template> for HTML, and <style> for CSS. They all live in one .vue file.

src/components/Greeting.vue
<script setup>
import { ref } from 'vue'

// ref() creates a reactive variable
const name = ref('World')
const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <div class="greeting">
    <h1>Hello, {{ name }}!</h1>
    
    <!-- v-model creates two-way binding -->
    <input v-model="name" placeholder="Enter your name" />
    
    <p>Count: {{ count }}</p>
    <button @click="increment">Add One</button>
  </div>
</template>

<style scoped>
.greeting {
  padding: 24px;
  font-family: sans-serif;
}
button {
  margin-top: 8px;
  padding: 8px 16px;
  cursor: pointer;
}
</style>
💡
The .value thing: When you read or write a ref inside <script setup>, you use name.value. Inside the template, Vue automatically unwraps it — so you just write {{ name }}. This trips everyone up once. Now you know.

Template Syntax Essentials

Vue's template is HTML with special directives (attributes starting with v-). These are the ones you'll use every day:

Vue Template
<script setup>
import { ref } from 'vue'

const show = ref(true)
const items = ref(['Apples', 'Bananas', 'Cherries'])
const selectedColor = ref('blue')
</script>

<template>
  <!-- v-if / v-else: conditional rendering -->
  <p v-if="show">Visible</p>
  <p v-else>Hidden</p>
  
  <!-- v-for: list rendering -->
  <ul>
    <li v-for="item in items" :key="item">{{ item }}</li>
  </ul>
  
  <!-- :attr (v-bind shorthand): dynamic attribute -->
  <div :class="selectedColor">Colored box</div>
  
  <!-- @event (v-on shorthand): event listener -->
  <button @click="show = !show">Toggle</button>
  
  <!-- v-model: two-way binding on input -->
  <select v-model="selectedColor">
    <option value="blue">Blue</option>
    <option value="red">Red</option>
  </select>
</template>

Using Components Inside Components

Import a component and use it as an HTML tag. With <script setup>, imported components are automatically available in the template — no registration step needed.

src/App.vue
<script setup>
import Greeting from './components/Greeting.vue'
</script>

<template>
  <main>
    <Greeting />
  </main>
</template>

Pass data to child components with props, and communicate back up with emits:

Child Component (UserCard.vue)
<script setup>
// defineProps: declare what data the parent can pass in
const props = defineProps({
  username: String,
  score: {
    type: Number,
    default: 0
  }
})

// defineEmits: declare events this component can fire
const emit = defineEmits(['delete'])
</script>

<template>
  <div class="card">
    <h3>{{ username }}</h3>
    <p>Score: {{ score }}</p>
    <button @click="emit('delete', username)">Remove</button>
  </div>
</template>
Parent Component
<script setup>
import UserCard from './components/UserCard.vue'
import { ref } from 'vue'

const users = ref([
  { username: 'alice', score: 42 },
  { username: 'bob', score: 17 }
])

function removeUser(name) {
  users.value = users.value.filter(u => u.username !== name)
}
</script>

<template>
  <UserCard
    v-for="user in users"
    :key="user.username"
    :username="user.username"
    :score="user.score"
    @delete="removeUser"
  />
</template>
ℹ️
The pattern is: props down, events up. Parent sends data to children via props. Children communicate back to parents via emits. This one-way data flow prevents bugs that come from multiple components mutating the same data.
📝 Day 1 Exercise
Build a Task List App
  1. Create a new Vue project with npm create vite@latest.
  2. Build a TaskList.vue component with a ref array of tasks (strings).
  3. Add an input + button to add new tasks to the list.
  4. Render each task with v-for, with a delete button next to each one.
  5. Add a checkbox per task using v-model to toggle completed state. Cross out completed tasks with CSS.
  6. Import and use your component in App.vue.

Day 1 Summary

  • Use Vite to create Vue 3 projects — it's fast and official.
  • <script setup> is the modern way to write components. Use it everywhere.
  • ref() creates reactive variables. Access .value in scripts, not in templates.
  • Core directives: v-if, v-for, v-model, :attr (v-bind), @event (v-on).
  • Data flows down via props, back up via emits. Keep it one-directional.
Finished this lesson?