Vue.js is a popular open-source JavaScript framework used for building user interfaces and single-page applications. If you already understood the basics of HTML, CSS and Javascript/Typescript, then using VueJS is going to be all familiar to you. In this tutorial, we'll learn how to build interactive web apps using Vue. You can find the project's source code here.
Technical Prerequisite
It is required that you have the following tools installed in order to follow this tutorial.
- Latest VSCode (or an IDE of your choice).
- Latest NodeJS.
- Latest Yarn (or any other Javascript package manager).
Knowledge Prerequisite
A basic understanding of HTML, CSS and Javascript/Typescript is required for this tutorial.
Create Vue Project
The first thing we're going to do is create a new Vue project:
- Now open your VSCode, press
Ctrl + Jto open the terminal and typeyarn create vue@latestinto the terminal. PressEnterkey. - Answer the prompts as below. Use arrow keys to navigate left/right when selecting your answers:
- When you're done, type
cd todo && yarn installto install the project dependencies. - Now type
yarn add sassto add support for SCSS. - Type
code .to load the project in VSCode. Notice the . after "code". - Once VSCode opens your new project, press
Ctrl + Jand typeyarn dev. Your project will be launched on http://localhost:5173/. - If all went well, then you should see a page like below in your page.
- Press
Ctrl + Cfrom the terminal to close your project server.
Project Structure
Before we go further, let me quckly introduce you to the src folder in your project.
-
App.vueis the base vue Single File Component(SFC) that holds your project together. Every component (or vue file) is an SFC. We'll talk about SFCs later but keep in mind that a Vue Component is a reusable and self-contained unit of Vue file that encapsulates a piece of UI functionality. For example, to create a custom reusable Button, you may create a simple Vue Component likeAppButton.vuewhich will contain all the layout, styling and functionalities of the button. - The
src/componentsfolder is where you'll keep all your small units of UI (components). - The
src/viewsfolder is where you'll keep your app pages. - The
src/routeris where we configure the app navigation i.e, which route leads to which page. -
src/storesis where we write app's global states. You'll learn more about this later. -
src/assetsis where we keep static assets like fonts, css and sometimes, images.

Technically, every box in the image is a vue component or SFC but we generally refer to Component.vue boxes as "components". In this structure, App.vue is the main app, HomeView.vue and AboutView.vue are different pages within the app and every Component.vue is a vue-styled UI component within each page. This is similar to how HTML DOM tree is structured: parents, children, siblings...etc.
Prepare to Build
Now let's take some steps to prepare our project for building:
- Go to
src/componentsfolder and delete all the files and folders inside as we won't be needing them. - Now go to
src/viewsfolder and deleteAboutView.vuefile. We won't be needing that as well. - Our app router (navigator) will try to locate
AboutView.vueand we get an error because we've deleted the file. To fix this, go torouter/index.tsand delete the lines as in the red box:
- Now go to
src/views/HomeView.vueand replace the existing code with this:
<template>
<div></div>
</template>
- Go to
src/App.vueand replace the existing code with this code:
<template>
<main>
<RouterView />
</main>
</template>
- We'll be using Google font Poppins in our app. We can import this font from Google's CDN. Go to
src/assets/main.cssand paste this at the top of the file@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap');. Then add this CSS code to the file (below all import statements):
body {
font-family: 'Poppins', sans-serif;
color: rgb(100, 99, 99);
}
Now your project should be empty and you're ready to start building.
Create Your First SFC
It's time to create your First UI component, an SFC. Vue's Single File Component allows you to define the HTML, CSS and Javascript sections of your UI in just one file. You define the HTML in template tag, CSS in style tag and Javascript in script tag. Let's see how that works:
- Create a file
src/components/TodoCard.vue; - Put this HTML section in the file:
<template>
<div class="todo_card">
<h3 class="task_title">Buy groceries</h3>
<p class="task_details">
I'll be visiting FoodCo this afternoon to stock more Milk, eggs, bread and fruits for this
week. Don't forget to get Elubo and Garri😂
</p>
<div class="tdc_footer">
<div class="task_cat"></div>
<div class="task_done">
<input type="checkbox" />
<p>Done</p>
</div>
</div>
</div>
</template>
- Now add this CSS section after the
templatetag:
<style lang="scss" scoped>
.todo_card {
width: 370px;
font-size: 0.92rem;
background-color: rgba(255, 255, 0, 0.289);
padding: 18px 28px;
border-radius: 12px;
box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
.task_title {
font-size: 1.2rem;
font-weight: 500;
}
.task_details {
margin: 8px auto 24px auto;
height: 70px;
}
.tdc_footer {
display: flex;
align-items: center;
justify-content: space-between;
.task_cat {
width: 24px;
height: 24px;
border-radius: 50%;
background-color: rgba(137, 43, 226, 0.308);
}
.task_done {
display: inline-flex;
align-items: center;
column-gap: 8px;
cursor: pointer;
}
}
}
</style>
Notice the scoped attribute on the style tag? This tells Vue to apply the style to elements ONLY in this particular component. If you want the style to be applied globally, remove the scoped attribute. Save your file.
Now let's import our component and display it on HomeView.
- Go to
src/views/HomeView.vueand add the typescript section to importTodoCard.vue:
<script setup lang="ts">
import TodoCard from '@/components/TodoCard.vue'
</script>
Did you notice the setup attribute on the script tag? This makes the script tag to use Vue's Composition API which we'll use to interact with Vue's reactive APIs. More of this later.
Now use TodoCard component in the template of HomeView:
<template>
<div>
<TodoCard />
</div>
</template>
Save HomeView file and run yarn dev in the terminal to relaunch your app.
Now we can say HomeView is the parent component of TodoCard because the former now hosts the latter.
Interpolation and Data Binding
It's time to share data between the HTML and Javascript sections of your component. The is known as data interpolation or data binding. It means you can declare a variable in script tag and attach its value to an HTML element in template tag for display. For example, let's create our task data as Javascript variables and render them in HTML. Go to src/components/TodoCard.vue and script tag below the style tag:
<script setup lang="ts">
const taskTitle = 'Buy groceries'
const taskDetails =
"I'll be visiting FoodCo this afternoon to stock more Milk, eggs, bread and fruits for this week. Don't forget to get Elubo and Garri😂"
</script>
Now replace the original contents in HTML with those Javascript variables this way:
<template>
<div class="todo_card">
<h3 class="task_title">{{ taskTitle }}</h3>
<p class="task_details">{{ taskDetails }}</p>
<div class="tdc_footer">
<div class="task_cat"></div>
<div class="task_done">
<input type="checkbox" />
<p>Done</p>
</div>
</div>
</div>
</template>
Save your file and you'll see your app is working. Notice how we surround the Javascript variable name with double curly brackers {{ }} to distinguish from literal HTML content. Of course we can combine this with raw HTML contents:
<h3 class="task_title">Task title is {{ taskTitle }} for this task</h3>
We can also bind Javascript variable to HTML attributes. Ok, let's say our h3 tag has an id attribute:
<h3 id="task-title" class="task_title">{{ taskTitle }}</h3>
We can pass attribute value from Javascript to this tag using vue's v-bind directive shorthand:
<template>
...
<h3 :id="taskId" class="task_title">{{ taskTitle }}</h3>
...
</template>
<script setup lang="ts">
const taskId = "task-001"
...
</script>
With this, the id attribute of the h3 tag will now be "task-001". Notice the : before the attribute. That's how we tell HTML that we're about to pass Javascript as the attribute's value. This can help us to manipulate HTML contents dynamically as we'll see soon.
Props
Let's say we want to define a task object in the parent component HomeView and display the object using TodoCard. So instead of rendering our task data directly from TodoCard, we define an object that represents this data in HomeView and pass it to TodoCard. We can achieve this with props. Props is how we pass data from parent components to child components:
- Define
taskobject inHomeView. We still have the same data as before, except we now have it in an object with an extradonemember to indicate whether the task is done. We'll use this later:
<template>
<div>
<TodoCard />
</div>
</template>
<script setup lang="ts">
import TodoCard from '@/components/TodoCard.vue'
const task = {
title: 'Buy groceries',
details:
"I'll be visiting FoodCo this afternoon to stock more Milk, eggs, bread and fruits for this week. Don't forget to get Elubo and Garri😂",
done: false
}
</script>
- Now we need to pass this data down to
TodoCardbut first, we should define the props signature thatTodoCardmust accept. Update thescriptsection ofTodoCard.vue:
<script setup lang="ts">
defineProps<{
title: string
details: string
done: boolean
}>()
</script>
- Now any parent component that uses
TodoCardmust supply values to these props as attributes. Let's updateTodoCardtemplate to display thepropsdata:
<template>
<div class="todo_card">
<h3 class="task_title">{{ title }}</h3>
<p class="task_details">{{ details }}</p>
<div class="tdc_footer">
<div class="task_cat"></div>
<div class="task_done">
<input type="checkbox" :checked="done" />
<p>Done</p>
</div>
</div>
</div>
</template>
- Update our usage of
TodoCardinHomeViewto receive the props:
<template>
<div>
<TodoCard :title="task.title" :details="task.details" :done="task.done" />
</div>
</template>
Iteration
Our task at the moment is just one hardcoded task object. But we'll have multiple tasks in our app. We can simulate this by creating an array of tasks and display them all using the same TodoCard component and v-for directive.
- In
HomeView, let's create atasksarray that contains 3 task objects:
<script setup lang="ts">
import TodoCard from '@/components/TodoCard.vue'
const tasks = [
{
title: 'Buy groceries',
details:
"I'll be visiting FoodCo this afternoon to stock more Milk, eggs, bread and fruits for this week. Don't forget to get Elubo and Garri😂",
done: false
},
{
title: 'Finish homework',
details: 'Math assignment and essay due tomorrow. I have to complete them tonight.',
done: true
},
{
title: 'Call mom',
details: "Wish her a happy birthday and remind her to get dad's pair of glasses.",
done: false
}
]
</script>
- Now let's use vue's v-for directive to iterate over each task object in
tasksand pass them toTodoCardfor display:
<template>
<div>
<TodoCard
v-for="(task, index) in tasks"
:key="index"
:title="task.title"
:details="task.details"
:done="task.done"
/>
</div>
</template>
Notice how we add an extra key attribute that accepts index, which is the unique index of an item in the array. The key attribute is required by vue to uniquely identified each item in the DOM when you create a loop using the v-for directive.
We need to lay our items out horizontally. Let's place them in a CSS flex container.
- Create
stylesection inHomeView:
<style lang="scss" scoped>
.task_list {
display: flex;
flex-wrap: wrap;
column-gap: 16px;
row-gap: 16px;
width: 100%;
}
</style>
- Now update the
templateso the contents are placed withintask_list:
<template>
<div class="task_list">
<TodoCard
v-for="(task, index) in tasks"
:key="index"
:title="task.title"
:details="task.details"
:done="task.done"
/>
</div>
</template>
- Lastly, update
src/assets/main.cssso that this is all you have:
@import './base.css';
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap');
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
body {
font-family: 'Poppins', sans-serif;
color: rgb(100, 99, 99);
}
We're not done exploring! Proceed to Part 2.
If you would like to connect, please contact me.






Top comments (0)