Learning coding means GreatToCode Be more than a Coder ! Greattocode , Join GreatToCode Community,1000+ Students Trusted On Us .If You want to learn coding, Then GreatToCode Help You.No matter what It Takes !


CODE YOUR WAY TO A MORE FULFILLING And HIGHER PAYING CAREER IN TECH, START CODING FOR FREE Camp With GreatToCode - Join the Thousands learning to code with GreatToCode
Interactive online coding classes for at-home learning with GreatToCode . Try ₹Free Per Month Coding Classes With The Top Teachers . VUE TUTORIALS

VUE TUTORIALS

 Vue is a JavaScript framework. It can be added to an HTML page with a <script> tag.

Vue extends HTML attributes with Directives, and binds data to HTML with Expressions.

Vue is a JavaScript Framework

Vue is a front-end JavaScript framework written in JavaScript.

Similar frameworks to Vue are React and Angular, but Vue is more lightweight and easier to start with.

Vue is distributed as a JavaScript file, and can be added to a web page with a script tag:

<script
  src="https://unpkg.com/vue@3/dist/vue.global.js"
>

</script>

Why Learn Vue?

  • It is simple and easy to use.
  • It is able to handle both simple and complex projects.
  • Its growing popularity and open-source community support.
  • In normal JavaScript we need to write HOW HTML and JavaScript is connected, but in Vue we simply need to make sure that there IS a connection and let Vue take care of the rest.
  • It allows for a more efficient development process with a template-based syntax, two-way data binding, and a centralized state management.

If some of these points are hard to understand, don't worry, you will understand at the end of the tutorial.


The Options API

There are two different ways to write code in Vue: The Options API and The Composition API.

The underlying concepts are the same for both the Options API and Composition API, so after learning one, you can easily switch to the other.

The Options API is what is written in this tutorial because it is considered to be more beginner-friendly, with a more recognizable structure.

Take a look at this page at the end of this tutorial to learn more about the differences between the Options API and the Composition API.


My first page

We will now learn how we can create our very first Vue web page, in 5 basic steps:

  1. Start with a basic HTML file.
  2. Add a <div> tag with id="app" for Vue to connect with.
  3. Tell the browser how to handle Vue code by adding a <script> tag with a link to Vue.
  4. Add a <script> tag with the Vue instance inside.
  5. Connect the Vue instance to the <div id="app"> tag.

These steps are described in detail below, with the full code in a 'Try It Yourself' example in the end.


Step 1: HTML page

Start with a simple HTML page:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>My first Vue page</title>
</head>
<body>

</body>
</html>

Step 2: Add a <div>

Vue needs an HTML element on your page to connect to.

Put a <div> tag inside the <body> tag and give it an id:

<body>
  <div id="app"></div>
</body>

Step 3: Add a link to Vue

To help our browser to interpret our Vue code, add this <script> tag:

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

Step 4: Add the Vue instance

Now we need to add our Vue code.

This is called the Vue instance and can contain data and methods and other things, but now it just contains a message.

On the last line in this <script> tag our Vue instance is connected to the <div id="app"> tag:

<div id="app"></div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<script>

  const app = Vue.createApp({
    data() {
      return {
        message: "Hello World!"
      }
    }
  })

 app.mount('#app')

</script>

Step 5: Display 'message' with Text Interpolation

Finally, we can use text interpolation, a Vue syntax with double curly braces {{ }} as a placeholder for data.

<div id="app"> {{ message }} </div>

The browser will exchange {{ message }} with the text stored in the 'message' property inside the Vue instance.

Here is our very first Vue page:

Example: My first Vue page!Get your own Vue Server

Test this code with the 'Try it Yourself' button below.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>My first Vue page</title>
</head>
<body>

  <div id="app">
    {{ message }}
  </div>

  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

  <script>
    const app = Vue.createApp({
      data() {
        return {
          message: "Hello World!"
        }
      }
    })

   app.mount('#app')

  
</script>
</body>
</html>
Try it Yourself »

Text Interpolation

Text interpolation is when text is taken from the Vue instance to show on the web page.

The browser receives the page with this code inside:

<div id="app"> {{ message }} </div>

Then the browser finds the text inside the 'message' property of the Vue instance and translates the Vue code into this:

<div id="app">Hello World!</div>

JavaScript in Text Interpolation

Simple JavaScript expressions can also be written inside the double curly braces {{ }}.

Example

Use JavaScript syntax to add a random number to the message inside the div element:

<div id="app">
  {{ message }} <br>
  {{'Random number: ' + Math.ceil(Math.random()*6) }}
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<script>

  const app = Vue.createApp({
    data() {
      return {
        message: "Hello World!"
      }
    }
  })

 app.mount('#app')

</script>
Try it Yourself »

Vue directives are special HTML attributes with the prefix v- that give the HTML tag extra functionality.

Vue directives connect to the Vue instance to create dynamic and reactive user interfaces.

With Vue, creating responsive pages is much easier and requires less code compared to traditional JavaScript methods.

Different Vue Directives

The different Vue directives we use in this tutorial are listed below.

DirectiveDetails
v-bindConnects an attribute in an HTML tag to a data variable inside the Vue instance.
v-ifCreates HTML tags depending on a condition. Directives v-else-if and v-else are used together with the v-if directive.
v-showSpecifies if an HTML element should be visible or not depending on a condition.
v-forCreates a list of tags based on an array in the Vue instance using a for-loop.
v-onConnects an event on an HTML tag to a JavaScript expression or a Vue instance method. We can also define more specifically how our page should react to a certain event by using event-modifiers.
v-modelUsed in HTML forms with tags like <form><input> and <button>. Creates a two way binding between an input element and a Vue instance data property.

Example: The v-bind DirectiveGet your own Vue Server

The browser finds what class to connect the <div> element to from the Vue instance.

<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    .pinkBG {
      background-color: lightpink;
    
}
  
</style>
</head>
<body>

  <div id="app">
    <div v-bind:class="vueClass"></div>
  </div>

  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          vueClass: "pinkBG"
        }
      }
    })
    app.mount('#app')
  
</script>
</body>
</html>
Try it Yourself »

Note: The example above could be written much simpler without the Vue code, but be patient. The real benefits of Vue can be seen in later examples when we make pages that are responsive.


Learning Vue at W3Schools

When learning Vue at W3Schools.com, you can use our "Try it Yourself" tool, which shows both the code and the result. This will make it easier for you to understand every part as we move forward.

You have already seen that a basic Vue setup consists of a Vue instance and that we can access it from the <div id="app"> tag with {{ }} or the v-bind directive.

On this page we will explain the v-bind directive in more detail.

The v-bind Directive

The v-bind directive lets us bind an HTML attribute to data in the Vue instance. This makes it easy to change the attribute value dynamically.

SyntaxGet your own Vue Server

<div v-bind:[attribute]="[Vue data]"></div>

Example

The src attribute value of an <img> tag is taken from the Vue instance data property 'url':

<img v-bind:src="url">
Try it Yourself »

CSS Binding

We can use the v-bind directive to do in-line styling and modify classes dynamically. We will show you briefly how to do that in this section, and later in this tutorial, on the CSS Binding page, we will explain this in more detail.


Bind style

In-line styling with Vue is done by binding the style attribute to Vue with v-bind.

As a value to the v-bind directive, we can write a JavaScript object with the CSS property and value:

Example

The font size depends on the Vue data property 'size'.

<div v-bind:style="{ fontSize: size }">
  Text example
</div>
Try it Yourself »

We can also separate the font size number value from the font size unit if we want to, like this:

Example

The font size number value is stored the Vue data property 'size'.

<div v-bind:style="{ fontSize: size + 'px' }">
  Text example
</div>
Try it Yourself »

We could also write the CSS property name with CSS syntax (kebab-case) in hyphens, but it is not recommended:

Example

The CSS property fontSize is referred to as 'font-size'.

<div v-bind:style="{ 'font-size': size + 'px' }">
  Text example
</div>
Try it Yourself »

Example

The background color depends on the 'bgVal' data property value inside the Vue instance.

<div v-bind:style="{ backgroundColor: 'hsl('+bgVal+',80%,80%)' }">
  Notice the background color on this div tag.
</div>
Try it Yourself »

Example

The background color is set with a JavaScript conditional (ternary) expression depending on whether the 'isImportant' data property value is 'true' or 'false'.

<div v-bind:style="{ backgroundColor: isImportant ? 'lightcoral' : 'lightgray' }">
  Conditional background color
</div>
Try it Yourself »

Bind class

We can use v-bind to change the class attribute.

The value of v-bind:class can be a variable:

Example

The class name is taken from the 'className' Vue data property:

<div v-bind:class="className">
  The class is set with Vue
</div>
Try it Yourself »

The value of v-bind:class can also be an object, where the class name will only take effect if it is set to 'true':

Example

The class attribute is assigned or not depending on if the class 'myClass' is set to 'true' or 'false':

<div v-bind:class="{ myClass: true }">
  The class is set conditionally to change the background color
</div>
Try it Yourself »

When the value of v-bind:class is an object, the class can be assigned depending on a Vue property:

Example

The class attribute is assigned depending on the 'isImportant' property, if it is 'true' or 'false':

<div v-bind:class="{ myClass: isImportant }">
  The class is set conditionally to change the background color
</div>
Try it Yourself »

Shorthand for v-bind

The shorthand for 'v-bind:' is simply ':'.

Example

Here we just write ':' instead of 'v-bind:':

<div :class="{ impClass: isImportant }">
  The class is set conditionally to change the background color
</div>
Try it Yourself »

We will continue to use v-bind: syntax in this tutorial to avoid confusion.

It is a lot easier to create an HTML element depending on a condition in Vue with the v-if directive than with plain JavaScript.

With Vue you just write the if-statement directly in the HTML element you want to create conditionally. It's that simple.

Conditional Rendering in Vue

Conditional rendering in Vue is done by using the v-ifv-else-if and v-else directives.

Conditional rendering is when an HTML element is created only if a condition is true, i.e. create the text "In stock" if a variable is 'true', or 'Not in stock' if that variable is 'false'.

ExampleGet your own Vue Server

Write different messages depending on whether there are any typewriters in stock or not:

<p v-if="typewritersInStock">
  in stock
</p>

<p v-else>
  not in stock
</p>
Try it Yourself »

Conditions in Vue

A condition, or "if-statement", is something that is either true or false.

A condition is often a comparison check between two values like in the example above to see if one value is greater than the other.

  • We use comparison operators like <>= or !== to do such checks.

  • Comparison checks can also be combined with logical operators such as && or ||.

  • Go to our JavaScript tutorial page to find out more about JavaScript comparisons.

We can use the number of typewriters in storage with a comparison check to decide if they are in stock or not:

Example

Use a comparison check to decide whether to write "In stock" or "Not in stock" depending on the number of typewriters in storage.

<p v-if="typewriterCount > 0">
  in stock
</p>

<p v-else>
  not in stock
</p>
Try it Yourself »

Directives for Conditional Rendering

This overview describes how the different Vue directives used for conditional rendering are used together.

DirectiveDetails
v-ifCan be used alone, or with v-else-if and/or v-else. If the condition inside v-if is 'true', v-else-if or v-else are not considered.
v-else-ifMust be used after v-if or another v-else-if. If the condition inside v-else-if is 'true', v-else-if or v-else that comes after are not considered.
v-elseThis part will happen if the first part of the if-statement is false. Must be placed at the very end of the if-statement, after v-if and v-else-if.

To see an example with all three directives shown above, we can expand the previous example with v-else-if so that the user sees 'In stock', 'Very few left!' or 'Out of stock':

Example

Use a comparison check to decide whether to write "In stock", "Very few left!" or "Not in stock" depending on the number of typewriters in storage.

<p v-if="typewriterCount>3">
  In stock
</p>

<p v-else-if="typewriterCount>0">
  Very few left!
</p>

<p v-else>
  Not in stock
</p>
Try it Yourself »

Use The Return Value from a Function

Instead of using a comparison check with the v-if directive, we can use the 'true' or 'false' return value from a function:

Example

If a certain text contains the word 'pizza', create a <p> tag with an appropriate message. The 'includes()' method is a native JavaScript method that checks if a text contain certain words.

<div id="app">
  <p v-if="text.includes('pizza')">The text includes the word 'pizza'</p>
  <p v-else>The word 'pizza' is not found in the text</p>
</div>
data() {
  return {
    text: 'I like taco, pizza, Thai beef salad, pho soup and tagine.'
  }
}
Try it Yourself »

The example above can be expanded to show that v-if also can create other tags like <div> and <img> tags:

Example

If a certain text contains the word 'pizza', create a <div> tag with a pizza image and a <p> tag with a message. The 'includes()' method is a native JavaScript method that check if a text contain certain words.

<div id="app">
  <div v-if="text.includes('pizza')">
    <p>The text includes the word 'pizza'</p>
    <img src="img_pizza.svg">
  </div>
  <p v-else>The word 'pizza' is not found in the text</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        text: 'I like taco, pizza, Thai beef salad, pho soup and tagine.'
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Below the example is expanded even more.

Example

If a certain text contains the word 'pizza' or 'burrito' or none of these words, different images and texts will be created.

<div id="app">
  <div v-if="text.includes('pizza')">
    <p>The text includes the word 'pizza'</p>
    <img src="img_pizza.svg">
  </div>
  <div v-else-if="text.includes('burrito')">
    <p>The text includes the word 'burrito', but not 'pizza'</p>
    <img src="img_burrito.svg">
  </div>
  <p v-else>The words 'pizza' or 'burrito' are not found in the text</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        text: 'I like taco, pizza, Thai beef salad, pho soup and tagine.'
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

With Vue we can now write code that create elements under certain conditions in a much easier way than with traditional JavaScript.

Learn how to make an element visible or not with v-show.

v-show is easy to use because the condition is written right in the HTML tag attribute.

Conditional Visibility

The v-show directive hides an element when the condition is 'false' by setting the CSS 'display' property value to 'none'.

After writing v-show as an HTML attribute we must give a conditon to decide to have the tag visible or not.

SyntaxGet your own Vue Server

<div v-show="showDiv">This div tag can be hidden</div>

In the code above, 'showDiv' represents a boolean Vue data property with either 'true' or 'false' as property value. If 'showDiv' is 'true' the div tag is shown, and if it is 'false' the tag is not shown.

Example

Display the <div> element only if the showDiv property is set to 'true'.

<div id="app">
  <div v-show="showDiv">This div tag can be hidden</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        showDiv: true
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

v-show vs. v-if

The difference between v-show and v-if is that v-if creates (renders) the element depending on the condition, but with v-show the element is already created, v-show only changes its visibility.

Therefore, it is better to use v-show when switching visibility of an object, because it is easier for the browser to do, and it can lead to a faster response and better user experience.

A reason for using v-if over v-show is that v-if can be used with v-else-if and v-else.

In the example below v-show and v-if are used separately on two different <div> elements, but based on the same Vue property. You can open the example and inspect the code to see that v-show keeps the <div> element, and only sets the CSS display property to 'none', but v-if actually destroys the <div> element.

Example

Display the two <div> elements only if the showDiv property is set to 'true'. If the showDiv property is set to 'false' and we inspect the example page with the browser we can see that the <div> element with v-if is destroyed, but the <div> element with v-show has just CSS display property set to 'none'.

<div id="app">
  <div v-show="showDiv">Div tag with v-show</div>
  <div v-if="showDiv">Div tag with v-if</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        showDiv: true
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

With normal JavaScript you might want to create HTML elements based on an array. You use a for-loop, and inside you need to create the elements, adjust them, and then add each element to your page. And these elements will not react to a change in the array.

With Vue you start with the HTML element you want to create into a list, with v-for as an attribute, refer to the array inside the Vue instance, and let Vue take care of the rest. And the elements created with v-for will automatically update when the array changes.

List Rendering

List rendering in Vue is done by using the v-for directive, so that several HTML elements are created with a for-loop.

Below are three slightly different examples where v-for is used.

ExampleGet your own Vue Server

Display a list based on the items of an array.

<ol>
  <li v-for="x in manyFoods">{{ x }}</li>
</ol>
Try it Yourself »

Loop Through an Array

Loop through an array to display different images:

Example

Show images of food, based on an array in the Vue instance.

<div>
  <img v-for="x in manyFoods" v-bind:src="x">
</div>
Try it Yourself »

Loop Through Array of Objects

Loop through an array of objects and display the object properties:

Example

Show both images and names of different types of food, based on an array in the Vue instance.

<div>
  <figure v-for="x in manyFoods">
    <img v-bind:src="x.url">
    <figcaption>{{ x.name }}</figcaption>
  </figure>
</div>
Try it Yourself »

Get the index

The index number of an array element can be really useful in JavaScript for-loops. Luckily we can get the index number when using v-for in Vue as well.

To get the index number with v-for we need to give two comma separated words in parentheses: the first word will be the array element, and the second word will be the index of that array element.

Example

Show index number and food name of elements in the 'manyFoods' array in the Vue instance.

<p v-for="(x, index) in manyFoods">
  {{ index }}: "{{ x }}<br>
</p>
Try it Yourself »

We can also display both array element index and information from the array element itself, if the array elements are objects:

Example

Show both the array element index number, and text from the objects in the 'manyFoods' array.

<p v-for="(x, index) in manyFoods">
  {{ index }}: "{{ x.name }}", url: "{{ x.url }}<br>
</p>
Try it Yourself »

Event handling in Vue is done with the v-on directive, so that we can make something happen when for example a button is clicked.

Event handling is when HTML elements are set up to run a certain code when a certain event happens.

Events in Vue are easy to use and will make our page truly responsive.

Vue methods are code that can be set up to run when an event happens.

With v-on modifiers you can describe in more detail how to react to an event.

Get startet with events

Lets start with an example to show how we can click a button to count moose in a forest.

We need:

  1. A button
  2. v-on on the <button> tag to listen to the 'click' event
  3. Code to increase the number of moose
  4. A property (variable) in the Vue instance to hold the number of moose
  5. Double curly braces {{}} to show the increased number of moose

ExampleGet your own Vue Server

Click the button to count one more moose in the forest. The count property increases each time the button is clicked.

<div id="app">
  <img src="img_moose.jpg">
  <p>{{ "Moose count: " + count }}</p>
  <button v-on:click="count++">Count moose</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        count: 0
      }
    }
  })
 app.mount('#app')
</script>
Try it Yourself »

Note: A benefit that comes with Vue is that the number of moose in the <p> tag is updated automatically. With plain JavaScript we would need to update the number the user sees with an additional line of code.


Events

There are lots of events we can use as triggers for running code, among the most common ones are: 'click', 'mouseover', 'mouseout', 'keydown' and 'input'.

For a complete list of events to use you can visit our HTML DOM Events page.


'v-on'

The v-on directive allows us to create pages that respond to what the user does.

The Vue v-on works by telling the browser what event to listen to, and what to do when that event occurs.


Methods

If we want to run more complex code when an event occurs we can put the code in a Vue method and refer to this method from the HTML attribute, like this:

<p v-on:click="changeColor">Click me</p>

Event Modifiers

In addition to v-on and Vue methods we can use something called event modifiers to modify an event so that it for example only happens once after a page is loaded, or modify an event like 'submit' to prevent a form from being submitted.


Learn More

As we can see, there are three techniques we need to learn about to use events in Vue:

  1. The Vue v-on directive
  2. Vue methods
  3. Vue v-on modifiers

Like event handling in plain JavaScript, the v-on directive in Vue tells the browser:

  1. which event to listen to ('click', 'keydown', 'mouseover', etc)
  2. what to do when that event occurs

Examples using v-on

Let's take a look at some examples to see how v-on can be used with different events and different code to run when these events occur.

Note: To run more advanced code when an event occurs we need to introduce Vue methods. Learn about Vue methods on the next page in this tutorial.


onclick Event

The v-on directive allows us to perform actions based on specified events.

Use v-on:click to perform action when the element is clicked.

ExampleGet your own Vue Server

The v-on directive is used on the <button> tag to listen to the 'click' event. When the 'click' event occurs the 'lightOn' data property is toggled between 'true' and 'false', making the yellow <div> behind the lightbulb visible/hidden.

<div id="app">
  <div id="lightDiv">
    <div v-show="lightOn"></div>
    <img src="img_lightBulb.svg">
  </div>
  <button v-on:click="lightOn = !lightOn">Switch light</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        lightOn: false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

oninput Event

Use v-on:input to perform action when the element gets an input, like a keystroke inside a text field.

Example

Count the number of keystroke for a input text field:

<div id="app">
  <input v-on:input="inpCount++">
  <p>{{ 'Input events occured: ' + inpCount }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        inpCount: 0
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

mousemove Event

Use v-on:mousemove to perform action when the mouse pointer moves over an element.

Example

Change the background color of a <div> element whenever the mouse pointer moves over it:

<div id="app">
  <p>Move the mouse pointer over the box below</p>
  <div v-on:mousemove="colorVal=Math.floor(Math.random()*360)"
       v-bind:style="{backgroundColor:'hsl('+colorVal+',80%,80%)'}"
>

  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        colorVal: 50
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Use v-on in a v-for Loop

You can also use the v-on directive inside a v-for loop.

The items of the array are available for each iteration inside the v-on value.

Example

Display a list based on the food array and add an click event for each item that will use a value from the array item to change the source of an image.

<div id="app">
  <img v-bind:src="imgUrl">
  <ol>
    <li v-for="food in manyFoods" v-on:click="imgUrl=food.url">
      {{ food.name }}
    </li>
  </ol>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        imgUrl: 'img_salad.svg',
        manyFoods: [
          {name: 'Burrito', url: 'img_burrito.svg'},
          {name: 'Salad', url: 'img_salad.svg'},
          {name: 'Cake', url: 'img_cake.svg'},
          {name: 'Soup', url: 'img_soup.svg'}
        ]
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Shorthand for v-on

The shorthand for 'v-on' is simply '@'.

Example

Here we just write '@' instead of 'v-on':

<button @:click="lightOn = !lightOn">Switch light</button>
Try it Yourself »

We will start using @ syntax a little later in this tutorial.

Like event handling in plain JavaScript, the v-on directive in Vue tells the browser:

  1. which event to listen to ('click', 'keydown', 'mouseover', etc)
  2. what to do when that event occurs

Examples using v-on

Let's take a look at some examples to see how v-on can be used with different events and different code to run when these events occur.

Note: To run more advanced code when an event occurs we need to introduce Vue methods. Learn about Vue methods on the next page in this tutorial.


onclick Event

The v-on directive allows us to perform actions based on specified events.

Use v-on:click to perform action when the element is clicked.

ExampleGet your own Vue Server

The v-on directive is used on the <button> tag to listen to the 'click' event. When the 'click' event occurs the 'lightOn' data property is toggled between 'true' and 'false', making the yellow <div> behind the lightbulb visible/hidden.

<div id="app">
  <div id="lightDiv">
    <div v-show="lightOn"></div>
    <img src="img_lightBulb.svg">
  </div>
  <button v-on:click="lightOn = !lightOn">Switch light</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        lightOn: false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

oninput Event

Use v-on:input to perform action when the element gets an input, like a keystroke inside a text field.

Example

Count the number of keystroke for a input text field:

<div id="app">
  <input v-on:input="inpCount++">
  <p>{{ 'Input events occured: ' + inpCount }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        inpCount: 0
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

mousemove Event

Use v-on:mousemove to perform action when the mouse pointer moves over an element.

Example

Change the background color of a <div> element whenever the mouse pointer moves over it:

<div id="app">
  <p>Move the mouse pointer over the box below</p>
  <div v-on:mousemove="colorVal=Math.floor(Math.random()*360)"
       v-bind:style="{backgroundColor:'hsl('+colorVal+',80%,80%)'}"
>

  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        colorVal: 50
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Use v-on in a v-for Loop

You can also use the v-on directive inside a v-for loop.

The items of the array are available for each iteration inside the v-on value.

Example

Display a list based on the food array and add an click event for each item that will use a value from the array item to change the source of an image.

<div id="app">
  <img v-bind:src="imgUrl">
  <ol>
    <li v-for="food in manyFoods" v-on:click="imgUrl=food.url">
      {{ food.name }}
    </li>
  </ol>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        imgUrl: 'img_salad.svg',
        manyFoods: [
          {name: 'Burrito', url: 'img_burrito.svg'},
          {name: 'Salad', url: 'img_salad.svg'},
          {name: 'Cake', url: 'img_cake.svg'},
          {name: 'Soup', url: 'img_soup.svg'}
        ]
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Shorthand for v-on

The shorthand for 'v-on' is simply '@'.

Example

Here we just write '@' instead of 'v-on':

<button @:click="lightOn = !lightOn">Switch light</button>
Try it Yourself »

We will start using @ syntax a little later in this tutorial.

Vue methods are functions that belong to the Vue instance under the 'methods' property.

Vue methods are great to use with event handling (v-on) to do more complex things.

Vue methods can also be used to do other things than event handling.

The Vue 'methods' Property

We have already used one Vue property in this tutorial, the 'data' property, where we can store values.

There is another Vue property called 'methods' where we can store functions that belong to the Vue instance. A method can be stored in the Vue instance like this:

const app = Vue.createApp({
  data() {
    return {
      text: ''
    }
  },
  methods: {
    writeText() {
      this.text = 'Hello Wrold!'
    }
  }
})

Tip: We need to write this. as prefix to refer to a data property from inside a method.

To call the 'writeText' method when we click the <div> element we can write the code below:

<div v-on:click="writeText"></div>

The example looks like this:

ExampleGet your own Vue Server

The v-on directive is used on the <div> element to listen to the 'click' event. When the 'click' event occurs the 'writeText' method is called and the text is changed.

<div id="app">
  <p>Click on the box below:</p>
  <div v-on:click="writeText">
    {{ text }}
  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        text: ''
      }
    },
    methods: {
      writeText() {
        this.text = 'Hello World!'
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Call a Method with the Event Object

When an event occurs so that a method is called, the event object is passed with the method by default. This is very convenient because the event object contains a lot of useful data, like for example the target object, the event type, or the mouse position when the 'click' or 'mousemove' event occured.

Example

The v-on directive is used on the <div> element to listen to the 'mousemove' event. When the 'mousemove' event occurs the 'mousePos' method is called and the event object is sent with the method by default so we can get the mouse pointer position.

We must use the this. prefix to refer to "xPos" inside the Vue instance data property from the method.

<div id="app">
  <p>Move the mouse pointer over the box below:</p>
  <div v-on:mousemove="mousePos"></div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        xPos: 0,
        yPos: 0
      }
    },
    methods: {
      mousePos(event) {
        this.xPos = event.offsetX
        this.yPos = event.offsetY
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

If we expand the example above by just one line, we can also make the background color change based on the mouse pointer position in the x-direction. The only thing we need to add is v-bind to change the backgound-color in the style attribute:

Example

The difference here from the example above is that the background color is bound to 'xPos' with v-bind so that hsl 'hue' value is set equal to 'xPos'.

<div
  v-on:mousemove="mousePos"
  v-bind:style="{backgroundColor:'hsl('+xPos+',80%,80%)'}"
>

</div>
Try it Yourself »

In the example below the event object carries a text from the <textarea> tag to make it look like we are writing inside a notebook.

Example

The v-on directive is used on the <textarea> tag to listen to the 'input' event which occurs whenever there is a change in the text inside the textarea element.

When the 'input' event occurs the 'writeText' method is called and the event object is sent with the method by default so we can get the text from the <textarea> tag. The 'text' property in the Vue instance is updated by the 'writeText' method. A span element is set up to show the 'text' value with the double curly braces syntax, and this is updated automatically by Vue.

<div id="app">
  <textarea v-on:input="writeText" placeholder="Start writing.."></textarea>
  <span>{{ text }}</span>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        text: ''
      }
    },
    methods: {
      writeText(event) {
        this.text = event.target.value
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Passing Arguments

Sometimes we want to pass an argument with the method when an event occurs.

Lets say you work as a forest ranger, and you want to keep count of moose sightings. Sometimes one or two moose are seen, other times over 10 moose might be seen during a day. We add buttons to count sightings '+1' and '+5', and a '-1' button in case we have counted too many.

In this case we can use the same method for all three buttons, and just call the method with a different number as an argument from the different buttons. This is how we can call a method with an argument:

<button v-on:click="addMoose(5)">+5</button>

And this is how the 'addMoose' method looks like:

methods: {
  addMoose(number) {
    this.count = this.count + number
  }
}

Lets see how passing an argument with a method works in a full example.

Example

<div id="app">
  <img src="img_moose.jpg">
  <p>{{ "Moose count: " + count }}</p>
  <button v-on:click="addMoose(+1)">+1</button>
  <button v-on:click="addMoose(+5)">+5</button>
  <button v-on:click="addMoose(-1)">-1</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        count: 0
      }
    },
    methods: {
      addMoose(number) {
        this.count+=number
      }
    }
  })
 app.mount('#app')
</script>
Try it Yourself »

Passing both an Argument and The Event Object

If we want to pass both the event object and another argument, there is a reserved name '$event' we can use where the method is called, like this:

<button v-on:click="addAnimal($event, 5)">+5</button>

And this is how the method in the Vue instance looks like:

methods: {
  addAnimal(e, number) {
    if(e.target.parentElement.id==="tigers"){
      this.tigers = this.tigers + number
    }
  }
}

Now let us look at an example to see how to pass both the event object and another argument with a method.

Example

In this example our method receives both the event object and a text.

<div id="app">
  <img
    src="img_tiger.jpg"
    id="tiger"
    v-on:click="myMethod($event,'Hello')"
>

  <p>"{{ msgAndId }}"</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        msgAndId: ''
      }
    },
    methods: {
      myMethod(e,msg) {
        this.msgAndId = msg + ', '
        this.msgAndId += e.target.id
      }
    }
  })
 app.mount('#app')
</script>
Try it Yourself »

Larger Example

In this example we see that it is possible to use only one method to count three different animals with three different increments for each animal. We acheive this by passing both the event object and the increment number:

Example

Both the increment size and the event object are passed as arguments with the method when a button is clicked. The reserved word '$event' is used to pass the event object with the method to tell what animal to count.

<div id="app">
  <div id="tigers">
    <img src="img_tiger.jpg">
    <button v-on:click="addAnimal($event,1)">+1</button>
    <button v-on:click="addAnimal($event,5)">+5</button>
    <button v-on:click="addAnimal($event,1)">-1</button>
  </div>
  <div id="moose">
    <img src="img_moose.jpg">
    <button v-on:click="addAnimal($event,1)">+1</button>
    <button v-on:click="addAnimal($event,5)">+5</button>
    <button v-on:click="addAnimal($event,1)">-1</button>
  </div>
  <div id="kangaroos">
    <img src="img_kangaroo.jpg">
    <button v-on:click="addAnimal($event,1)">+1</button>
    <button v-on:click="addAnimal($event,5)">+5</button>
    <button v-on:click="addAnimal($event,1)">-1</button>
  </div>
  <ul>
    <li>Tigers: {{ tigers }} </li>
    <li>Moose: {{ moose }} </li>
    <li>Kangaroos: {{ kangaroos }} </li>
  </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        tigers: 0,
        moose: 0,
        kangaroos: 0
      }
    },
    methods: {
      addAnimal(e,number) {
        if(e.target.parentElement.id==="tigers") {
          this.tigers+=number
        }
        else if(e.target.parentElement.id==="moose") {
          this.moose+=number
        }
        else {
          this.kangaroos+=number
        }
      }
    }
  })
 app.mount('#app')
</script>
Try it Yourself »

Event modifiers in Vue modify how events trigger the running of methods and help us handle events in a more efficient and straightforward way.

Event modifiers are used together with the Vue v-on directive, to for example:

  • Prevent the default submit behavior of HTML forms (v-on:submit.prevent)
  • Make sure that an event can only run once after the page is loaded (v-on:click.once)
  • Specify what keyboard key to use as an event to run a method (v-on:keyup.enter)

How To Modify The v-on Directive

Event modifiers are used to define how to react on an event in more detail.

We use event modifiers by first connecting a tag to an event like we have seen before:

<button v-on:click="createAlert">Create alert</button>

Now, to define more specifically that the button click event should only fire one time after the page loads, we can add the .once modifier, like this:

<button v-on:click.once="createAlert">Create alert</button>

Here is an example with the .once modifier:

ExampleGet your own Vue Server

The .once modifier is used on the <button> tag to only run the method the first time the 'click' event happens.

<div id="app">
  <p>Click the button to create an alert:</p>
  <button v-on:click.once="creteAlert">Create Alert</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    methods: {
      createAlert() {
        alert("Alert created from button click")
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Note: It is also possible to handle an event inside the method instead of using event modifiers, but event modifiers make it a lot easier.


Different v-on Modifiers

Event modifiers are used in different situations. We can use event modifiers when we listen to keyboard events, mouse click events, and we can even use event modifiers in combination with each other.

The event modifier .once can be used after both keyboard and mouse click events.


Keyboard Key Event Modifiers

We have three different keyboard event types keydownkeypress, and keyup.

With each key event type, we can specify exactly what key to listen to after a key event occurs. We have .space.enter.w and .up to name a few.

You can write the key event to the web page, or to the console with console.log(event.key), to find the value of a certain key yourself:

Example

The keydown keyboard event triggers the 'getKey' method, and the value 'key' from the event object is written to the console and to the web page.

<input v-on:keydown="getKey">
<p> {{ keyValue }} </p>
data() {
  return {
    keyValue = ''
  }
},
methods: {
  getKey(evt) {
    this.keyValue = evt.key
    console.log(evt.key)
  }
}
Try it Yourself »

We can also choose to limit the event to happen only when a mouse click or a key press happens in combination with system modifier keys .alt.ctrl.shift or .meta. The system modifier key .meta represents the Windows key on Windows computers, or command key on Apple computers.

Key ModifierDetails
.[Vue key alias]The most common keys have their own aliases in Vue:
  • .enter
  • .tab
  • .delete
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
.[letter]Specify the letter that comes when you press the key. As an example: use the .s key modifier to listen to the 'S' key.
.[system modifier key].alt.ctrl.shift or .meta. These keys can be used in combination with other keys, or in combination with mouse clicks.

Example

Use the .s modifier to create an alert when the user writes an 's' inside the <textarea> tag.

<div id="app">
  <p>Try pressing the 's' key:</p>
  <textarea v-on:keyup.s="createAlert"></textarea>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    methods: {
      createAlert() {
        alert("You pressed the 'S' key!")
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Combine Keyboard Event Modifiers

Event modifiers can also be used in combination with each other so that more than one thing must happen simultaneous for the method to be called.

Example

Use the .s and .ctrl modifiers in combination to create an alert when 's' and 'ctrl' are pressed simultaneously inside the <textarea> tag.

<div id="app">
  <p>Try pressing the 's' key:</p>
  <textarea v-on:keydown.ctrl.s="createAlert"></textarea>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    methods: {
      createAlert() {
        alert("You pressed the 'S' and 'Ctrl' keys, in combination!")
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Mouse Button Modifiers

To react on a mouse click, we can write v-on:click, but to specify which mouse button that was clicked, we can use .left.center or .right modifiers.

Trackpad users: You might need to click with two fingers, or in the lower right hand side of the trackpad on your computer to create a right click.

Example

Change the background color when a user right-clicks in the <div> element:

<div id="app">
  <div v-on:click.right="changeColor"
       v-bind:style="{backgroundColor:'hsl('+bgColor+',80%,80%)'}"
>

    <p>Press right mouse button here.</p>
  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        bgColor: 0
      }
    },
    methods: {
      changeColor() {
        this.bgColor+=50
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Mouse button events can also work in combination with a system modifier key.

Example

Change the background color when a user right-clicks in the <div> element in combination with the 'ctrl' key:

<div id="app">
  <div v-on:click.right.ctrl="changeColor"
       v-bind:style="{backgroundColor:'hsl('+bgColor+',80%,80%)'}"
>

    <p>Press right mouse button here.</p>
  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        bgColor: 0
      }
    },
    methods: {
      changeColor() {
        this.bgColor+=50
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

The event modifier .prevent can be used in addition to the .right modifier to prevent the default drop-down menu to appear when we right click.

Example

The drop-down menu is prevented from appearing when you right click to change the background color of the <div> element:

<div id="app">
  <div v-on:click.right.prevent="changeColor"
       v-bind:style="{backgroundColor:'hsl('+bgColor+',80%,80%)'}"
>

    <p>Press right mouse button here.</p>
  </div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        bgColor: 0
      }
    },
    methods: {
      changeColor() {
        this.bgColor+=50
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

It would be possible to prevent the drop-down menu from appearing after right click by using event.preventDefault() inside the method, but with the Vue .prevent modifier the code becomes more readable and easier to maintain.

You can also react on left button mouse clicks in combination with other modifiers, like click.left.shift:

Example

Hold the 'shift' keyboard key and press left mouse button on the <img> tag to change image.

<div id="app">
  <p>Hold 'Shift' key and press left mouse button:</p>
  <img v-on:click.left.shift="changeImg" v-bind:src="imgUrl">
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        imgUrlIndex: 0,
        imgUrl: 'img_tiger_square.jpeg',
        imgages: [
          'img_tiger_square.jpeg',
          'img_moose_square.jpeg',
          'img_kangaroo_square.jpeg'
        ]
      }
    },
    methods: {
      changeImg() {
        this.imgUrlIndex++
        if(this.imgUrlIndex>=3){
          this.imgUrlIndex=0
        }
        this.imgUrl = this.images[this.imgUrlIndex]
      }
    }
  })
  app.mount('#app')
</script>

Vue gives us an easy way to improve the user experience with forms by adding extra functionality like responsiveness and form validation.

Vue uses the v-model directive when handling forms.

Our First Form with Vue

Lets start with a simple shopping list example to see how Vue can be used when creating a form.

For more information about forms in HTML, with related tags and attributes, see our HTML Forms tutorial.


1. Add standard HTML form elements:

<form>
  <p>Add item</p>
  <p>Item name: <input type="text" required></p>
  <p>How many: <input type="number"></p>
  <button type="submit">Add item</button>
</form>

2. Create the Vue instance with the current item name, number and the shopping list, and use v-model to connect our inputs to it.

<div id="app">
  <form>
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <button type="submit">Add item</button>
  </form>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        shoppingList: [
          { name: 'Tomatoes', number: 5 }
        ]
      }
    }
  })
  app.mount('#app')
</script>

3. Call the method to add an item to the shopping list, and prevent the default browser refresh on submit.

<form v-on:submit.prevent="addItem">

4. Create the method that adds the item to the shopping list, and clears the form:

methods: {
  addItem() {
    let item = {
      name: this.itemName,
      number: this.itemNumber
      }
    this.shoppingList.push(item);
    this.itemName = null
    this.itemNumber = null
  }
}

5. Use v-for to show an automatically updated shopping list below the form:

<p>Shopping list:</p>
<ul>
  <li v-for="item in shoppingList">{{item.name}}{{item.number}}</li>
</ul>

Below is the final code for our first Vue form.

ExampleGet your own Vue Server

In this example we can add new items to a shopping list.

<div id="app">
  <form v-on:submit.prevent="addItem">
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <button type="submit">Add item</button>
  </form>

  <p>Shopping list:</p>
  <ul>
    <li v-for="item in shoppingList">{{item.name}}{{item.number}}</li>
  </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        shoppingList: [
          { name: 'Tomatoes', number: 5 }
        ]
      }
    },
    methods: {
      addItem() {
        let item = {
          name: this.itemName,
          number: this.itemNumber
        }
        this.shoppingList.push(item)
        this.itemName = null
        this.itemNumber = null
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Notice the two-way binding v-model provides in the example above:

  • v-model updates the Vue instance data when the HTML input change
  • v-model also updates the HTML input when the Vue instance data changes

Compared to normal JavaScript, it is easier to work with forms in Vue because the v-model directive connects with all types of input elements in the same way.

v-model creates a link between the input element value attribute and a data value in the Vue instance. When you change the input, the data updates and when the data changes, the input updates as well (two-way binding).

Two-way Binding

As we have already seen in the shopping list example on the previous page, v-model provides us with a two-way binding, meaning that the form input elements update the Vue data instance, and a change in the Vue instance data updates the inputs.

The example below also demonstrates the two-way binding with v-model.

ExampleGet your own Vue Server

Two-way binding: Try to write inside the input field to see that the Vue data property value gets updated. Try also to write directly in the code to change the Vue data property value, run the code, and see how the input field is updated.

<div id="app">
  <input type="text" v-model="inpText">
  <p> {{ inpText }} </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        inpText: 'Initial text'
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Note: The v-model two-way binding functionality could actually be achieved with a combination of v-bind:value and v-on:input:

  • v-bind:value to update the input element from the Vue instance data,
  • and v-on:input to update the Vue instance data from the input.

But v-model is much easier to use so that is what we will do.


A Dynamic Checkbox

We add a checkbox to our shopping list on the previous page to mark if an item is important or not.

Next to the checkbox we add a text that allways reflects the current 'important' status, changing dynamically between 'true' or 'false'.

We use v-model to add this dynamic checkbox and text to improve user interaction.

We need:

  • a boolean value in the Vue instance data property called 'important'
  • a checkbox where the user can check if the item is important
  • a dynamic feedback text so that the user can see if the item is important

Below is how the 'important' feature looks, isolated from the shopping list.

Example

The checkbox text is made dynamic so that the text reflects the current checkbox input value.

<div id="app">
  <form>
    <p>
      Important item?
      <label>
        <input type="checkbox" v-model="important">
        {{ important }}
      </label>
    </p>
  </form>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
       important: false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Let's include this dynamic feature in our shopping list example.

Example

<div id="app">
  <form v-on:submit.prevent="addItem">
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <p>
      Important?
      <label>
        <input type="checkbox" v-model="itemImportant">
        {{ important }}
      </label>
    </p>
    <button type="submit">Add item</button>
  </form>
  <hr>
  <p>Shopping list:</p>
  <ul>
    <li v-for="item in shoppingList">{{item.name}}{{item.number}}</li>
  </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        important: false,
        shoppingList: [
          { name: 'Tomatoes', number: 5, important: false }
        ]
      }
    },
    methods: {
      addItem() {
        let item = {
          name: this.itemName,
          number: this.itemNumber
          important: this.itemImportant
        }
        this.shoppingList.push(item)
        this.itemName = null
        this.itemNumber = null
        this.itemImportant = false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Mark Found Items in The Shopping List

Let's add functionality so that items added to the shopping list can be marked as found.

We need:

  • the list items to react on click
  • to change the status of the clicked item to 'found', and use this to visually move the item away and strike it through with CSS

We create one list with all items we need to find, and one list below with items found striked through. We can actually put all the items in the first list, and all the items in the second list, and just use v-show with the Vue data property 'found' to define whether to show the item in the first or second list.

Example

After adding items to the shopping list we can pretend to go shopping, clicking the items away after finding them. If we click an item by mistake we can take it back to the 'not found' list by clicking the item once more.

<div id="app">
  <form v-on:submit.prevent="addItem">
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <p>
      Important?
      <label>
        <input type="checkbox" v-model="itemImportant">
        {{ important }}
      </label>
    </p>
    <button type="submit">Add item</button>
  </form>

  <p><strong>Shopping list:</strong></p>
  <ul id="ulToFind">
    <li v-for="item in shoppingList"
        v-bind:class="{ impClass: item.important }"
        v-on:click="item.found=!item.found"
        v-show="!item.found"
>

          {{ item.name }}{{ item.number}}
    </li>
  </ul>
  <ul id="ulFound">
    <li v-for="item in shoppingList"
        v-bind:class="{ impClass: item.important }"
        v-on:click="item.found=!item.found"
        v-show="item.found"
>

          {{ item.name }}{{ item.number}}
    </li>
  </ul>

</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        important: false,
        shoppingList: [
          { name: 'Tomatoes', number: 5, important: false, found: false }
        ]
      }
    },
    methods: {
      addItem() {
        let item = {
          name: this.itemName,
          number: this.itemNumber,
          important: this.itemImportant,
          found: false
        }
        this.shoppingList.push(item)
        this.itemName = null
        this.itemNumber = null
        this.itemImportant = false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Use v-model to make The Form Itself Dynamic

We can make a form where the customer orders from a menu. To make it easy for the customer, we only present the drinks to choose from after the customer chooses to order drinks. This is can be argued to be better than presenting the customer with all items from the menu at once. In this example we use v-model and v-show to make the form itself dynamic.

We need:

  • A form, with relevant input tags and 'Order' button.
  • Radio-buttons to select 'Dinner', 'Drink' or 'Dessert'.
  • After category is chosen, a dropdown menu appears with all the items in that category.
  • When an item is chosen you see an image of it, you can choose how many and add it to the order. The form is reset when the item is added to the order.

Example

This form is dynamic. It changes based on user choices. The user must first choose category, then product and how many, before the order button becomes visible and the user can order it.

Try it Yourself »

Learn more about how to use v-bind to modify CSS with the style and class attributes.

While the concept to change the style and class attributes with v-bind is fairly straight forward, the syntax might need some getting used to.

Dynamic CSS in Vue

You have already seen how we can use Vue to modify CSS by using v-bind on the style and class attributes. It has been explained briefly in this tutorial under v-bind and several examples with Vue changing CSS has also been given.

Here we will explain in more detail how CSS can be changed dynamically with Vue. But first lets look at two examples with techniques we have already seen in this tutorial: in-line styling with v-bind:style and assigning a class with v-bind:class


Inline Styling

We use v-bind:style to do in-line styling in Vue.

ExampleGet your own Vue Server

An <input type="range"> element is used to change the opacity of a <div> element with the use of in-line styling.

<input type="range" v-model="opacityVal">
<div v-bind:style="{ backgroundColor: 'rgba(155,20,20,'+opacityVal+')' }">
  Drag the range input above to change opacity here.
</div>
Try it Yourself »

Assign a Class

We use v-bind:class to assign a class to an HTML tag in Vue.

Example

Select images of food. Selected food is highlighted with the use of v-bind:class to show what you have selected.

<div v-for="(img, index) in images">
  <img v-bind:src="img.url"
       v-on:click="select(index)"
       v-bind:class="{ selClass: img.sel }"
>

</div>
Try it Yourself »

Other Ways to Assign Classes and Style

Here are different aspects regarding the use of v-bind:class and v-bind:style that we have not seen before in this tutorial:

  1. When CSS classes are assigned to an HTML tag with both class="" and v-bind:class="" Vue merges the classes.
  2. An object containing one or more classes is assigned with v-bind:class="{}". Inside the object one or more classes might be toggled on or off.
  3. With in-line styling (v-bind:style) camelCase is preferred when defining a CSS property, but 'kebab-case' can also be used if it is written inside quotes.
  4. CSS classes can be assigned with arrays / with array notation / syntax

These points are explained in more detail below.


1. Vue Merges 'class' And 'v-bind:class'

In cases when an HTML tag belongs to a class assigned with class="", and is also assigned to a class with v-bind:class="", Vue merges the classes for us.

Example

<div> element belongs to two classes: 'impClass' and 'yelClass'. The 'important' class is set the normal way with the class attribute, and 'yellow' class is set with v-bind:class.

<div class="impClass" v-bind:class="{yelClass: isYellow}">
  This div belongs to both 'impClass' and 'yelClass'.
</div>
Try it Yourself »

2. Assign More Than One Class With 'v-bind:class'

When assigning an HTML element to a class with v-bind:class="{}", we can simply use comma to separate and assign multiple classes.

Example

<div> element can belong to both 'impClass' and 'yelClass' classes, depending on the boolean Vue data properties 'isYellow' and 'isImportant'.

<div v-bind:class="{yelClass: isYellow, impClass: isImportant}">
  This tag can belong to both the 'impClass' and 'yelClass' classes.
</div>
Try it Yourself »

3. Camel case vs kebab case notation with 'v-bind:style'

When modifying CSS in Vue with in-line styling (v-bind:style), it is recommended to use camelCase notation for the CSS property, but 'kebab-case' can also be used if the CSS property is inside quotes.

Example

Here, we set CSS properties background-color and font-weight for a <div> element in two different ways: the recommended way with camelCase backgroundColor, and the not recommended way with 'kebab-case' in quotes 'font-weight'. Both alternatives work.

<div v-bind:style="{ backgroundColor: 'lightpink', 'font-weight': 'bolder' }">
  This div tag has pink background and bold text.
</div>
Try it Yourself »

'Camel case' and 'kebab case' notation are ways of writing a series of words without space or punctuation.

  • Camel case notation is when the first word starts with a small letter, and every word after starts with a capital letter, like 'backgroundColor' or 'camelCaseNotation'. It is called camel case because we can imagine every capital letter resembling a hump on a camels back.
  • Kebab case notation is when the words are separated with a dash -, like 'background-color' or 'kebab-case-notation'. It is called kebab case because we can imagine the dashes resembling the skewer in a 'shish kebab'.

4. Array Syntax with 'v-bind:class'

We can use array syntax with v-bind:class to add multiple classes. With array syntax we can use both classes that depend on a Vue property and classes that do not depend on a Vue property.

Example

Here, we set CSS classes 'impClass' and 'yelClass' with array syntax. The class 'impClass' depends on a Vue property isImportant and the class 'yelClass' is always attached to the <div> element.

<div v-bind:class="[{ impClass: isImportant }, 'yelClass' ]">
  This div tag belongs to one or two classes depending on the isImportant property.
</div>
Try it Yourself »

Computed properties are like data properties, except they depend on other properties.

Computed properties are written like methods, but they do not accept any input arguments.

Computed properties are updated automatically when a dependency changes, while methods are called on when something happens, like with event handling for example.

Computed properties are used when outputting something that depends on something else.

Computed Properties are Dynamic

The big advantage with a computed property is that it is dynamic, meaning it changes depending on for example the value of one or more data properties.

Computed properties is the third configuration option in the Vue instance that we will learn. The first two configuration options we have already looked at are 'data' and 'methods'.

As with 'data' and 'methods' computed properties also has a reserved name in the Vue instance: 'computed'.

SyntaxGet your own Vue Server

const app = Vue.createApp({
  data() {
    ...
  },
  computed: {
    ...
  },
  methods: {
    ...
  }
})

Computed Property 'yes' or 'no'

Let's say we want a form to create items in a shopping list, and we want to mark if a new item is important or not. We could add a 'true' or 'false' feedback when the checkbox gets checked, like we have done in an example before:

Example

An input element is made dynamic so that the text reflects the status.

<input type="checkbox" v-model="chbxVal"> {{ chbxVal }}
data() {
  return {
    chbxVal: false
  }
}
Try it Yourself »

However, if you you ask someone if something is important, they will most likely answer 'yes' or 'no' instead of 'true' or 'false'. So to make our form more fitting with normal language (more intuitive) we should have 'yes' or 'no' as feedback on the checkbox instead of 'true' or 'false'.

And guess what, a computed property is a perfect tool to help us with that.

Example

With a computed property 'isImportant' we can now customize the text feedback to the user when the checkbox is toggled on and off.

<input type="checkbox" v-model="chbxVal"> {{ isImportant }}
data() {
  return {
    chbxVal: false
  }
},
computed: {
  isImportant() {
    if(this.chbxVal){
      return 'yes'
    }
    else {
      return 'no'
  }
}
Try it Yourself »

Computed Properties vs. Methods

Computed properties and methods are both written as functions, but they are different:

  • Methods runs when called from HTML, but computed properties updates automatically when a dependency change.
  • Computed properties are used the same way we use data properties, but they are dynamic.

watcher is a method that watches a data property with the same name.

watcher runs every time the data property value changes.

Use a watcher if a certain data property value requires an action.

The Watcher Concept

Watchers is the fourth configuration option in the Vue instance that we will learn. The first three configuration options we have already looked at are 'data', 'methods' and 'computed'.

As with 'data', 'methods' and 'computed' watchers also has a reserved name in the Vue instance: 'watch'.

SyntaxGet your own Vue Server

const app = Vue.createApp({
  data() {
    ...
  },
  watch: {
    ...
  },
  computed: {
    ...
  },
  methods: {
    ...
  }
})

As mentioned in the green area at the top, a watcher monitors a data property with the same name.

We never call a watcher method. It is only called automatically when the property value changes.

The new property value is always available as an input argument to the watcher method, and so is the old value.

Example

An <input type="range"> element is used to change a value 'rangeVal'. A watcher is used to prevent the user from choosing values between 20 and 60 that are considered illegal.

<input type="range" v-model="rangeVal">
<p>{{ rangeVal }}</p>
const app = Vue.createApp({
  data() {
    rangeVal: 70
  },
  watch: {
    rangeVal(val){
      if( val>20 && val<60) {
        if(val<40){
          this.rangeVal = 20;
        }
        else {
          this.rangeVal = 60;
        }
      }
    }
  }
})
Try it Yourself »

A Watcher with New and Old Values

In addition to the new property value, the previous property value is also automatically available as an input argument to watcher methods.

Example

We set up click event on a <div> element to record mouse pointer x-position 'xPos' with a method 'updatePos'. A watcher calculates the difference in pixels between the new x-position and the previous with the use of old and new input arguments to the watcher method.

<div v-on:click="updatePos"></div>
<p>{{ xDiff }}</p>
const app = Vue.createApp({
  data() {
    xPos: 0,
    xDiff: 0
  },
  watch: {
    xPos(newVal,oldVal){
      this.xDiff = newVal-oldVal
    }
  },
  methods: {
    updatePos(evt) {
      this.xPos = evt.offsetX
    }
  }
})
Try it Yourself »

We can also use new and old values to give feedback to the user the exact moment the input goes from being invalid to valid:

Example

The value from an <input> element is connected to a watcher. If the value includes a '@' it is considered a valid e-mail address. The user gets a feedback text to inform if the input is valid, invalid, or if it just got valid with the last keystroke.

<input v-type="email" v-model="inpAddress">
<p v-bind:class="myClass">{{ feedbackText }}</p>
const app = Vue.createApp({
  data() {
    inpAddress: '',
    feedbackText: '',
    myClass: 'invalid'
  },
  watch: {
    inpAddress(newVal,oldVal) {
      if( !newVal.includes('@') ) {
        this.feedbackText = 'The e-mail address is NOT valid';
        this.myClass = 'invalid';
      }
      else if( !oldVal.includes('@') && newVal.includes('@') ) {
        this.feedbackText = 'Perfect! You fixed it!';
        this.myClass = 'valid';
      }
      else {
        this.feedbackText = 'The e-mail address is valid :)';
      }
    }
  }
})
Try it Yourself »

Watchers vs. Methods

Watchers and methods are both written as functions, but there are many differences:

  • Methods are called from HTML.
  • Methods are often called when an event happens.
  • Methods automatically receives the event object as an input.
  • We can also send other values we choose as an input to a method.
  • Watchers are only called when the watched data property value changes, and this happens automatically.
  • Watchers automatically receives the new and old value from the watched property.
  • We cannot choose to send any other values with a watcher as an input.

Watchers vs. Computed Properties

Watchers and computed properties are both written as functions.

Watchers and computed properties are both called automatically when a dependency change, and never called from HTML.

Here are some differences between computed properties and watchers:

  • Watchers only depend on one property, the property they are set up to watch.
  • Computed properties can depend on many properties.
  • Computed properties are used like data properties, except they are dynamic.
  • Watchers are not referred to from HTML.

template in Vue is what we call the HTML part of our Vue application.

The <template> tag will later be used in *.vue files to structure our code in a better way.

It is possible to use template as a configuration option in the Vue instance, and put the HTML code inside.

The Vue Template

Let's look at an example where we use 'template' as a configuration option. This is a simple example where we have just moved the HTML part into the configuration option 'template':

ExampleGet your own Vue Server

The HTML content from inside the <div id="app"> is moved to the configuration option 'template', encapsulated in backtick quotes `...`. We can write many lines of HTML inside a backtick quote.

<div id="app"></div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    template:
      `<h1>{{ message }}</h1>
      <p>This is a second line of HTML code, inside backtick quotes</p>`
,
    data() {
      return {
        message: "Hello World!"
      }
    }
  })
app.mount('#app')
</script>
Try it Yourself »

Single File Components (SFCs)

As you can see in the code example above, also the HTML part of our Vue application can be gathered inside the Vue instance, but this does not make it easier to get an overview in the HTML file.

To get a better overview, to make it easier to handle larger projects, and to get a better development environment, we will now switch to write our Vue code in SFCs, or *.vue files.

All *.vue files only consist of three parts:

  • <template> where the HTML content is.

  • <script> for our Vue code.

  • <style> where we write the CSS styling.

But before we can use *.vue files in our project we need to set up our computer in a different way. Scroll down in this tutorial will explain this.

Using *.vue files for our Vue project makes sense because:

  • it becomes easier to handle larger projects with the use of templates and components.
  • we can see and test our project through the https protocol, like the users will see the page.
  • the page updates immediately when changes are saved, without reloading.
  • this is how real web pages in Vue are built.
  • it is how developers work.

Why?

As we saw on the previous page about templates and components in Vue, there is now a need for different way to work because we want to:

  • have larger projects
  • gather all Vue related code in one place
  • use components in Vue (we will come to this soon)
  • have highlighting and auto-completion support in the editor
  • auto-update the browser

And to make all this possible we much switch to *.vue files.


How?

SFCs (Single File Components), or *.vue files, are easier to work with but cannot run directly in the browser, so we need to set up our computer to compile our *.vue files to *.html, *.css and *.js files so that the browser can run our Vue application.

To build our web page based on SFCs we use a program called Vite as the build tool, and we write our code in the VS Code editor with the Volar extension for Vue 3 language features.


Setup

Follow the three steps below to install what you need to run Vue SFC applications on your computer.


  1. The "VS Code" Editor

    There are many different editors that can be used for Vue projects. We use the VS Code editor. Download VS Code and install it.


  2. The VS Code "Volar" Extension

    To get highlighting and auto-completion with *.vue files in the editor, open VS Code, go to "Extensions" on the left hand side. Search for "Volar" and install the extension with the most downloads and the description "Language support for Vue 3".

    Screenshot Volar Extension
  3. Node.js

    Download and install the latest version of Node.js, as the Vue build tool "Vite" runs on top of this.

    Node.js is an open-source server-side JavaScript runtime environment.


Create The Default Example Project

Follow the steps below to create the default Vue example project on your computer.


  1. Create a folder for your Vue projects on your computer.


  2. In VS Code, open a terminal by choosing Terminal -> New Terminal from the menu:

    Screenshot New Terminal
  3. Use the terminal to navigate to the Vue folder you just created by using commands like cd <folder-name>cd ..ls (Mac/Linux) and dir (Windows). If you are not familiar with writing commands in the terminal, see our introduction to Command Line Interface (CLI) here.


  4. After you have navigated to your Vue folder in the terminal, write:

    npm init vue@latest

  5. Create your first project, with project name "firstsfc":


  6. Answer "No" to all options:


  7. Now you should be presented with this in your terminal:


  8. We will now run the commands as suggested above.

    Run this command to change directory to your new project inside the 'firstsfc' folder:

    cd firstsfc

  9. Install all required dependencies so that the Vue project works:

    npm install

  10. Start the development server:

    npm run dev

  11. The terminal window should now look like this:

    And your browser should open the example project automatically:

    If you cannot find the example project in the browser, use the link from the terminal. The link you find in your terminal window might have a different address than the address in the screenshot above.

    Now the example project is running on your machine in development mode by the Vite build tool.


The Project Files

The example project that has automatically been created contains many files, and we will take a quick look at a few of them.

main.js

Go to your Vue project in the VS Code editor, find the "main.js" file in the "src" folder:

"main.js" tells Vite how to build the Vue project based on the "App.vue" file. This is similar to how we previously gave a CDN link with the script tag to tell the browser how to run our Vue code, and how we mounted the Vue instance to the <div id="app"> tag.

App.vue

In the same example project folder, find the "App.vue" file and open it. Like all other *.vue files, "App.vue" contains three parts: a <script> part, a <template> part and a <style> part.

App.vue:

<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />
    </div>
  </header>

  <main>
    <TheWelcome />
  </main>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

As you can see in the script part of "App.vue", other *.vue files are referred to: those are 'components' and are located in the 'components' folder. If you take a look in the 'template' part of the 'App.vue' file, you can see tags that are not normal HTML tags: <HelloWorld> and <TheWelcome>. This is how the components are referred to. Components are like apps within the app. We will learn more about components soon.

To create our first SFC web page from scratch we will:

  1. Create a new clean Vue project
  2. Write code in the 'App.vue' file
  3. See how the web page updates automatically during development
  4. Build the page for production

Create a Clean Project

Now we will remove all content in the example project we made on the previous page to create our own simple web page in Vue.

Before we start to write code, remove all content inside the <template><script> and <style> tags, and remove any attributes like 'setup' or 'scoped'.

Your 'App.vue' file should now look like this:

App.vue:

<script></script>

<template></template>

<style></style>

Also remove the folders 'assets' and 'components' inside the 'src' folder.

Remove the line where assets are imported inside the 'main.js' file so that 'main.js' looks like this:

main.js:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

We now have an empty project to work with.


Write code in 'App.vue'

Now that we have a clean project, add a header inside the <template> tag, like this:

<template>
  <h1>Hello World!</h1>
</template>

<script></script>
<style></style>

Save the 'App.vue' file, go to your browser by following the localhost link in the terminal. Do you see the result? The browser should now update automatically every time you save a change in VS Code, without having to manually refresh the browser.

Now lets take a look at a slightly larger Vue example:

ExampleGet your own Vue Server

App.vue:

<template>
  <h1>{{ message }}</h1>
</template>

<script>
export default {
  data() {
    return {
      message: 'This is some text'
    };
  }
};
</script>

<style></style>
Run Example »

Note: In the example above, export default makes it possible for 'main.js' to catch the data with the import App from './App.vue' so that it can be mounted on the <div id="app"> tag inside 'index.html'.

Components in Vue lets us decompose our web page into smaller pieces that are easy to work with.

We can work with a Vue component in isolation from the rest of the web page, with its own content and logic.

A web page often consists of many Vue components.

What are Components?

Components are reusable and self-contained pieces of code that encapsulates a specific part of the user interface, so that we can make Vue applications that are scalable and easier to maintain.

We can make components in Vue ourselves, or use built-in components that we will learn about later, like <Teleport> or <KeepAlive>. Here we will focus on components we make ourselves.


Creating a Component

Components in Vue is a very powerful tool because it lets our web page become more scalable and bigger projects become easier to handle.

Let's make a component and add it to our project.

  1. Create a new folder components inside the src folder.

  2. Inside the components folder, create a new file FoodItem.vue. It is common to name components with PascalCase naming convention, without spaces and where all new words starts with a capital letter, also the first word.

  3. Make sure the FoodItem.vue file look like this:

Code inside the FoodItem.vue component:

<template>
  <div>
    <h2>{{ name }}</h2>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: 'Apples',
      message: 'I like apples'
    }
  }
};
</script>

<style></style>

As you can see in the example above, components also consist of <template><script> and <style> tags, just like our main App.vue file.


Adding The Component

Notice that the <script> tag in the example above start with export default. This means that the object containing the data properties can be received, or imported, in another file. We will use this to implement the FoodItem.vue component into our existing project by importing it with the main.js file.

First, rewrite the last line into two lines in your original main.js file:

main.js:

import { createApp } from 'vue'

import App from './App.vue'

const app = createApp(App)
app.mount('#app')

Now, add the FoodItem.vue component by inserting lines 4 and 7 in your main.js file:

main.js:

import { createApp } from 'vue'

import App from './App.vue'
import FoodItem from './components/FoodItem.vue'

const app = createApp(App)
app.component('food-item', FoodItem)
app.mount('#app')

On line 7, the component is added so that we can use it as a custom tag <food-item/> inside the <template> tag in our App.vue file like this:

App.vue:

<template>
  <h1>Food</h1>
  <food-item/>
  <food-item/>
  <food-item/>
</template>

<script></script>

<style></style>

And, let's add some styling inside the <style> tag in the App.vue file. Make sure the development server is running, and check out the result.

ExampleGet your own Vue Server

App.vue:

<template>
  <h1>Food</h1>
  <food-item/>
  <food-item/>
  <food-item/>
</template>

<script></script>

<style>
  #app > div {
    border: dashed black 1px;
    display: inline-block;
    margin: 10px;
    padding: 10px;
    background-color: lightgreen;
  }
</style>
Run Example »

Development mode: When working with your Vue projects, it is useful to always have your project in development mode by running the following code line in the terminal:

npm run dev

Individual Components

A very useful and powerful property when working with components in Vue is that we can make them behave individually, without having to mark elements with unique IDs like we must do with plain JavaScript. Vue automatically takes care to treat each component individually.

Let's make the <div> elements count when we click them.

The only thing added to our main application file App.vue is in CSS to have the cursor look like a hand pointing during hover to imply that there is some sort of click functionality.

CSS code added to the <style> tag in App.vue:

#app > div:hover {
  cursor: pointer;
}

In our component file FoodItem.vue we must add a data property count, a click listener to the <div> element, a method to run when click happens to increment the counter, and text interpolation {{}} to show the count.

Example

FoodItem.vue:

<template>
  <div v-on:click="countClicks">
    <h2>{{ name }}</h2>  
    <p>{{ message }}</p>
    <p id="red">You have clicked me {{ clicks }} times.</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: 'Apples',
      message: 'I like apples',
      clicks: 0
    }
  },
  methods: {
    countClicks() {
      this.clicks++;
    }
  }
}
</script>

<style>
  #red {
    font-weight: bold ;
    color: rgb(144, 12, 12);
  }
</style>
Run Example »

We don't have to define unique IDs or do any extra work for Vue to handle the counting individually for each <div> element, Vue just does this automatically.

But except for the different counter values, the content of the <div> elements is still the same. In the next page we will learn more about components so that we can use components in a way that makes more sense. For example it would make more sense to display different kind of food in each <div> element.

Props is a configuration option in Vue.

With props we can pass data to the components via custom attributes to the component tag.

Pass Data To a Component

Do you remember the example on the previous page where all three components said 'Apple'? With props we can now pass data down to our components to give them different content and make them look different.

Let's make a simple page to show 'Apples', 'Pizza' and 'Rice'.

In the main application file App.vue we create our own attribute 'food-name' to pass a prop with the <food-item/> component tags:

App.vue:

<template>
  <h1>Food</h1>
  <food-item food-name="Apples"/>
  <food-item food-name="Pizza"/>
  <food-item food-name="Rice"/>
</template>

<script></script>

<style>
  #app > div {
    border: dashed black 1px;
    display: inline-block;
    width: 120px;
    margin: 10px;
    padding: 10px;
    background-color: lightgreen;
  }
</style>

Receive Data Inside a Component

To receive the data sent via the 'food-item' attribute from App.vue we use this new 'props' configuration option. We list the attributes received so that our component *.vue file knows about them, and now we can use the props wherever we want in the same way as we use a data property.

FoodItem.vue:

<script>
  export default {
    props: [
      'foodName'
    ]
  }
</script>

Props attributes are written with a dash - to separate words (kebab-case) in the <template> tag, but kebab-case is not legal in JavaScript. So instead we need to write the attribute names as camelCase in JavaScript, and Vue understands this automatically!

Finally, our example with <div> elements for 'Apples', 'Pizza' and 'Rice' looks like this:

ExampleGet your own Vue Server

App.vue:

<template>
  <h1>Food</h1>
  <food-item food-name="Apples"/>
  <food-item food-name="Pizza"/>
  <food-item food-name="Rice"/>
</template>

FoodItem.vue:

<template>
  <div>
    <h2>{{ foodName }}</h2>
  </div>
</template>

<script>
  export default {
    props: [
      'foodName'
    ]
  }
</script>

<style></style>
Run Example »

Soon we will see how to pass different data types as props attributes to components, but before we do that, let's expand our code with a description of each type of food, and put the food <div> elements inside a Flexbox wrapper.

Example

App.vue:

<template>
  <h1>Food</h1>
  <div id="wrapper">
    <food-item
      food-name="Apples"
      food-desc="Apples are a type of fruit that grow on trees."/>
    <food-item
      food-name="Pizza"
      food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."/>
    <food-item
      food-name="Rice"
      food-desc="Rice is a type of grain that people like to eat."/>
  </div>
</template>

<script></script>

<style>
  #wrapper {
    display: flex;
    flex-wrap: wrap;
  }
  #wrapper > div {
    border: dashed black 1px;
    margin: 10px;
    padding: 10px;
    background-color: lightgreen;
  }
</style>

FoodItem.vue:

<template>
  <div>
    <h2>{{ foodName }}</h2>
    <p>{{ foodDesc }}</p>
  </div>
</template>

<script>
  export default {
    props: [
      'foodName',
      'foodDesc'
    ]
  }
</script>

<style></style>
Run Example »

Boolean Props

We can achieve different functionality by passing props of different data types, and we are able to define rules for how attributes are given when components are created from App.vue.

Let's add a new prop 'isFavorite'. This should be a boolean prop with value either true or false so that we can use it directly with v-show to display a favorite stamp <img> tag if the food is considered a favorite.

To pass props with a data type different to String, we must write v-bind: in front of the attribute we want to pass.

This is how we pass the boolean 'isFavorite' prop from App.vue as an attribute 'is-favorite':

App.vue:

<template>
  <h1>Food</h1>
  <p>My favorite food has a diploma image attached to it.</p>
  <div id="wrapper">
    <food-item
      food-name="Apples"
      food-desc="Apples are a type of fruit that grow on trees."
      v-bind:is-favorite="true"/>
    <food-item
      food-name="Pizza"
      food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."
      v-bind:is-favorite="false"/>
    <food-item
      food-name="Rice"
      food-desc="Rice is a type of grain that people like to eat."
      v-bind:is-favorite="false"/>
  </div>
</template>

We receive the boolean 'isFavorite' prop inside FoodItem.vue and show a favorite stamp if the food is considered favorite:

Example

FoodItem.vue:

<template>
  <div>
    <h2>
      {{ foodName }}
      <img src="/img_quality.svg" v-show="isFavorite">
    </h2>
    <p>{{ foodDesc }}</p>
  </div>
</template>

<script>
  export default {
      props: ['foodName','foodDesc','isFavorite']
  }
</script>

<style>
  img {
    height: 1.5em;
    float: right;
  }
</style>
Run Example »

Images: To make the image in the example above work locally in the project on your machine, open the example above, right click the image, choose "Save Image As..." and save it in the "public" folder in your project.


Props Interface

In the example above, based on the code inside FoodItem.vue, we cannot know for sure that we receive the 'isFavorite' prop, and we cannot know for sure if it is a boolean value. To help us with this we can define the data-type of props we receive, we can set props to be required, and we can even make validation functions to validate the props we receive.

Defining props we receive serves as a documentation for other people if you work in a team, and it provides us with warnings in the console if the rules that we have defined are broken.


Props as an Object

In FoodItem.vue, we comment out how we defined the props in an array to have it as reference, and instead define the props in an object. We can also define the data type of each prop in addition to the prop name, like this:

FoodItem.vue:

<script>
  export default {
    // props: ['foodName','foodDesc','isFavorite']
    props: {
      foodName: String,
      foodDesc: String,
      isFavorite: Boolean
    }
  }
</script>

With props defined in this way, other people can look inside FoodItem.vue and easily see what the component expects.

If a component is created from the parent element (in our case App.vue) and given a prop with the wrong data type, you get a warning in the console, like this:

Screenshot of wrong data type prop warning

Such warnings are useful to let ourselves and others know that a component is not used how it is supposed to, and to tell what is wrong so that we can correct the mistake.


Required Props

To tell Vue that a prop is required we need to define the prop as an object. Let's make the prop 'foodName' required, like this:

FoodItem.vue:

<script>
  export default {
    // props: ['foodName','foodDesc','isFavorite']
    props: {
      foodName: {
        type: String,
        required: true
      },
      foodDesc: String,
      isFavorite: Boolean
    }
  }
</script>

If a component is created from the parent element (in our case App.vue) and a required prop is not defined, you get a warning in the console, like this:

Screenshot of required prop warning

Such warnings are useful to let ourselves and others know that a component is not used how it is supposed to, and to tell what is wrong so that we can correct the mistake.


Default Value

We can set a default value for a prop.

Let's create a default value for the 'foodDesc' prop in the 'FoodItem' component, and then create such an item for rice without defining the 'foodDesc' prop:

Example

App.vue:

<template>
  <h1>Food</h1>
  <p>My favorite food has a diploma image attached to it.</p>
  <div id="wrapper">
    <food-item
      food-name="Apples"
      food-desc="Apples are a type of fruit that grow on trees."
      v-bind:is-favorite="true"/>
    <food-item
      food-name="Pizza"
      food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."
      v-bind:is-favorite="false"/>
    <food-item
      food-name="Rice"
      food-desc="Rice is a type of grain that people like to eat." 
      v-bind:is-favorite="false"/>
  </div>
</template>

FoodItem.vue:

<script>
  export default {
    props: {
      foodName: {
        type: String,
        required: true
      },
      foodDesc: {
        type: String,
        required: false,
        default: 'This is the default description.'
      }
      isFavorite: {
        type: Boolean,
        required: false,
        default: false
      }
    }
  }
</script>
Run Example »

Props Validator Function

We can also define a validator function that decides if the prop value is valid or not.

Such validator functions must return either true or false. When the validator returns false, it means the prop value is invalid. An invalid prop value generates a warning in the browser console when we run our page in developer mode, and the warning is a useful hint to make sure the components are used as intended.

Let's say we want the food description to be a certain length, between 20 and 50 characters. We can add a validator function to make sure the food description provided has a valid length.

FoodItem.vue:

<script>
  export default {
    props: {
      foodName: {
        type: String,
        required: true
      },
      foodDesc: {
        type: String,
        required: false,
        default: 'This is the default description.',
        validator: function(value) {
          if( 20<value.length && value.length<50 ) {
            return true;
          }
          else {
            return false;
          }
        }
      }
      isFavorite: {
        type: Boolean,
        required: false,
        default: false
      }
    }
  }
</script>

Note: If you add the validator code above to your local project, you will get a warning in development mode because the food description for pizza is 65 characters, which is 15 characters longer than the validator function allows.


Modify Props

When a component is created in the parent element we are not allowed to change the value of the prop received in the child element. So inside FoodItem.vue we cannot change the value of the 'isFavorite' prop we get from App.vue. The prop is read-only from the parent, which is App.vue in our case.

But let's say we want the user to be able to change what food is considered favorite by clicking a button. Now there is a need to change the 'isFavorite' prop, but we cannot do it because it is read only.

We are not allowed to change 'isFavorite'. This will generate an error.

methods: {
  toggleFavorite() { 
    this.isFavorite = !this.isFavorite;
  }
}

To get around this we can use the prop to initialize a new data value 'foodIsFavorite', inside FoodItem.vue, like this:

data() {
  return { 
    foodIsFavorite: this.isFavorite
  }
}

And now we can add a method so the user can toggle this new data value:

methods: {
  toggleFavorite() { 
    this.foodIsFavorite = !this.foodIsFavorite;
  }
}

We must also add the toggle button to each food item, and change v-show in the <img> tag to depend on the new data property 'foodIsFavorite'. And to make our example simpler we also slim down the props declaration to just an array.

Example

FoodItem.vue:

<template>
  <div>
    <h2>
      {{ foodName }}
      <img src="/img_quality.svg" v-show="foodIsFavorite">
    </h2>
    <p>{{ foodDesc }}</p>
    <button v-on:click="toggleFavorite">Favorite</button>
  </div>
</template>

<script>
  export default {
  props: ['foodName','foodDesc','isFavorite'],
  data() {
    return {
      foodIsFavorite: this.isFavorite
    }
  },
  methods: {
    toggleFavorite() {
      this.foodIsFavorite = !this.foodIsFavorite;
    }
  }
}
</script>

<style>
  img {
    height: 1.5em;
    float: right;
  }
</style>
Run Example »

Components can be reused with v-for to generate many elements of the same kind.

When generating elements with v-for from a component, it is also very helpful that props can be assigned dynamically based on values from an array.

Create Component Elements with v-for

We will now create component elements with v-for based on an array with food item names.

ExampleGet your own Vue Server

App.vue:

<template>
  <h1>Food</h1>
  <p>Components created with v-for based on an array.</p>
  <div id="wrapper">
    <food-item
      v-for="x in foods"
      v-bind:food-name="x"/>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        foods: ['Apples','Pizza','Rice','Fish','Cake']
      };
    }
  }
</script>

FoodItem.vue:

<template>
  <div>
    <h2>{{ foodName }}</h2>
  </div>
</template>

<script>
  export default {
    props: ['foodName']
  }
</script>
Run Example »

v-bind Shorthand

To bind props dynamically we use v-bind, and because we will use v-bind much more now than before we will use the v-bind: shorthand : in the rest of this tutorial.


The 'key' Attribute

If we modify the array after the elements are created with v-for, errors can emerge because of the way Vue updates such elements created with v-for. Vue reuses elements to optimize performance, so if we remove an item, already existing elements are reused instead of recreating all elements, and element properties might not be correct anymore.

The reason for elements being reused incorrectly is that elements do not have a unique identifier, and that is exactly what we use the key attribute for: to let Vue tell the elements apart.

We will generate faulty behavior without the key attribute, but first let's build a web page with foods using v-for to display: food name, description, image for favorite food and button to change favorite status.

Example

App.vue:

<template>
  <h1>Food</h1>
  <p>Food items are generated with v-for from the 'foods' array.</p>
  <div id="wrapper">
    <food-item
      v-for="x in foods"
      :food-name="x.name"
      :food-desc="x.desc"
      :is-favorite="x.favorite"/>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        foods: [
          { name: 'Apples',
            desc: 'Apples are a type of fruit that grow on trees.',
            favorite: true },
          { name: 'Pizza',
            desc: 'Pizza has a bread base with tomato sauce, cheese, and toppings on top.',
            favorite: false },
          { name: 'Rice',
            desc: 'Rice is a type of grain that people like to eat.',
            favorite: false }
          { name: 'Fish',
            desc: 'Fish is an animal that lives in water.',
            favorite: true }
          { name: 'Cake',
            desc: 'Cake is something sweet that tastes good.',
            favorite: false }
        ]
      };
    }
  }
</script>

<style>
  #wrapper {
    display: flex;
    flex-wrap: wrap;
  }
  #wrapper > div {
    border: dashed black 1px;
    flex-basis: 120px;
    margin: 10px;
    padding: 10px;
    background-color: lightgreen;
  }
</style>

FoodItem.vue:

<template>
  <div>
    <h2>
      {{ foodName }}
      <img src="/img_quality.svg" v-show="foodIsFavorite">
    </h2>
    <p>{{ foodDesc }}</p>
    <button v-on:click="toggleFavorite">Favorite</button>
  </div>
</template>

<script>
  export default {
    props: ['foodName','foodDesc','isFavorite'],
    data() {
      return {
        foodIsFavorite: this.isFavorite
      }
    },
    methods: {
      toggleFavorite() {
        this.foodIsFavorite = !this.foodIsFavorite;
      }
    }
  }
</script>

<style>
  img {
    height: 1.5em;
    float: right;
  }
</style>
Run Example »

To see that we need the key attribute, let's create a button that removes the second element in the array. When this happens, without the key attribute, the favorite image is transferred from the 'Fish' element to the 'Cake' element, and that is NOT correct:

Example

The only difference from the previous example is that we add a button:

<button @click="removeItem">Remove Item</button>

and a method:

methods: {
  removeItem() {
    this.foods.splice(1,1);
  }
}

in App.vue.

Run Example »

As mentioned before: this fault, that the favorite image changes from 'fish' to 'cake' when an element is removed, has to do with Vue optimizing the page by reusing elements, and at the same time Vue cannot fully tell the elements apart. That is why we should always include the key attribute to uniquely mark each element when generating elements with v-for. When we use the key attribute, we no longer get this problem.

We do not use the array element index as the key attribute value because that changes when array elements are removed and added. We could create a new data property to keep a unique value for each item, like an ID number, but because the food items already have unique names we can just use that:

Example

We only need to add one line in App.vue to uniquely identify each element created with v-for and fix the problem:

<food-item
  v-for="x in foods"
  :key="x.name"
  :food-name="x.name"
  :food-desc="x.desc"
  :is-favorite="x.favorite"
/>
Run Example »

With the built-in $emit() method in Vue we can create a custom event in the child component that can be captured in the parent element.

Props are used to send data from the parent element to the child component, and $emit() is used to do the oposite: to pass information from the child component to the parent.

The purpose of the things we will do next is to end up with the 'favorite' status of a food item to be changed in the parent App.vue instead of in the the FoodItem.vue child component where the change is currently happening.

The reason for changing the favorite status in App.vue instead of in FoodItem.vue is that App.vue is where the favorite status is stored in the first place, so that needs to be updated. In a larger project the data might come from a database we have connection to in App.vue, and we want a change happening from the component to make a change in the database, so we need to communicate back to the parent from the child component.

Emit a Custom Event

There is a need to send information from the component to the parent, and we use the built-in method $emit() to do that.

We already have the toggleFavorite method inside the FoodItem.vue component that runs when the toggle button is clicked. Now let's remove the existing line and add a line to emit our custom event 'toggle-favorite':

FoodItem.vue:

methods: {
  toggleFavorite() {
    this.foodIsFavorite = !this.foodIsFavorite;
    this.$emit('toggle-Favorite');
  }
}

We can choose the name of our custom event, but it is normal to use kebab-case for emit events.


Receive an Emit Event

The custom emit event 'toggle-favorite' is now emitted from the FoodItem.vue component, but we need to listen to the event in the App.vue parent and call a method that does something so that we can see that the event happened.

We listen to the event with the shorthand @ instead of v-on: in App.vue where the component is created:

ExampleGet your own Vue Server

Listen to the 'toggle-favorite' event in App.vue:

<food-item
  v-for="x in foods"
  :key="x.name"
  :food-name="x.name"
  :food-desc="x.desc"
  :is-favorite="x.favorite"
  @toggle-favorite="receiveEmit"
/>

When our custom 'toggle-favorite' event happens, we need to create the 'testEmit' method in App.vue so that we can see that the event happened:

methods: {
  receiveEmit() {
    alert('Hello World!');
  }
}
Run Example »

Change The Food Item 'favorite' Status in The Parent

We now have an event that notifies App.vue when the 'Favorite' button is clicked from the child component.

We want to change the 'favorite' property in the 'foods' array in App.vue for the correct food item when a 'Favorite' button is clicked. To do that we send the food item name from FoodItem.vue to App.vue because that is unique for each food item:

FoodItem.vue:

methods: {
  toggleFavorite() {
    this.$emit('toggle-favorite', this.foodName);
  }
}

We can now receive the food item name in App.vue as an argument to the method called when the 'toggle-favorite' event happens, like this:

Example

App.vue:

methods: {
  receiveEmit(foodId) {  
    alert( 'You clicked: ' + foodId );
  }
}
Run Example »

Now that we know what food item that was clicked we can update the 'favorite' status for the correct food item inside the 'foods' array:

App.vue:

methods: {
  receiveEmit(foodId) {
    const foundFood = this.foods.find(
      food => food.name === foodId
    );
    foundFood.favorite = !foundFood.favorite;
  }
}

In the code above, the array method 'find' goes through the 'foods' array and looks for an object with name property equal to the food item we have clicked, and returns that object as 'foundFood'. After that we can set 'foundFood.health' to be oposite to what it was before so that it toggles between true and false.

Learn more about the JavaScript array method 'find' here.

Learn more about JavaScript arrow functions here.

The correct food inside the 'foods' array now gets its 'favorite' status updated. The only thing remaining is to get the image indicating favorite food updated.

Because the food item components are already created with the 'favorite' status from the 'foods' array and sent as a prop 'is-favorite' from App.vue, we just need to refer to this 'isFavorite' prop in FoodItem.vue from v-show where the <img> element is to update the image:

<img src="/img_quality.svg" v-show="isFavorite">

We can also delete the 'foodIsFavorite' data property in FoodItem.vue because it is no longer in use.

Example

In this final example code the favorite status of the food items can be toggled in a similar way as before, but now the favorite status is modified in the correct place, inside App.vue.

Run Example »

The 'emits' Option

In the same way that we declare props inside the FoodItem.vue component, we can also document what the component emits by using the Vue 'emits' option.

Props must be declared in the component, while emits are just recommended to be documented.

This is how we can document our emit in the FoodItem.vue component:

<script>
export default {  
  props: ['foodName','foodDesc','isFavorite'],
  emits: ['toggle-favorite'],
  methods: {
    toggleFavorite() {
      this.$emit('toggle-favorite', this.foodName);
    }
  }
};
</script>

The component becomes easier for others to use when the emits are documented.

A component can be called with attributes that are not declared as props, and they will simply fall through to the root element in the component.

With fallthrough attributes you get a better overview from the parent where the component is created, and it simplifies our code because we don't need to declare the attribute as a prop.

Typical attributes used to fall through are classstyle and v-on.

Fallthrough Attributes

It can be nice to for example control the component styling from the parent rather than having the styling hidden away inside the component.

Let's create a new example, a basic todo list in Vue, and see how the style attribute falls through to the components representing things to do.

So, our App.vue should contain the list of things to do, and an <input> element and a <button> to add new things to do. Each list item is a <todo-item /> component.

App.vue:

<template>
  <h3>Todo List</h3>  
  <ul>
    <todo-item
      v-for="x in items"
      :key="x"
      :item-name="x"
    />
  </ul>
  <input v-model="newItem">
  <button @click="addItem">Add</button>
</template>

<script>
  export default {
    data() {
      return {
        newItem: '',
        items: ['Buy apples','Make pizza','Mow the lawn']
      };
    },
    methods: {
      addItem() {
        this.items.push(this.newItem),
        this.newItem = '';
      }
    }
  }
</script>

And TodoItem.vue just receives the description of what to do as a prop:

TodoItem.vue:

<template>
  <li>{{ itemName }}</li>
</template>

<script>
  export default {
    props: ['itemName']
  }
</script>

To build our application correctly we also need the right setup in main.js:

main.js:

import { createApp } from 'vue'
  
import App from './App.vue'
import TodoItem from './components/TodoItem.vue'

const app = createApp(App)
app.component('todo-item', TodoItem)
app.mount('#app')

To see the point of this section, that properties can fall through to the root element inside the <template> of our component, we can give the list items some styling from App.vue:

ExampleGet your own Vue Server

We give styling to the <li> elements inside the component, from App.vue:

<template>
  <h3>Todo List</h3>
  <ul>
    <todo-item
      v-for="x in items"
      :key="x"
      :item-name="x"
      style="background-color: lightgreen;"
    />
  </ul>
  <input v-model="newItem">
  <button @click="addItem">Add</button>
</template>
Run Example »

To confirm that the style attribute has actually fallen through we can right click an <li> element in our todo list in the browser, choose 'Inspect', and we can see the style attribute is now on the <li> element:


Merging 'class' and 'style' Attributes

If 'class' or 'style' attributes are already set, and 'class' or 'style' attributes also comes from the parent as fallthrough attributes, the attributes will be merged.

Example

In addition to the existing styling from the parent, we add a margin to the <li> elements inside the TodoItem.vue component:

<template>
  <li style="margin: 5px 0;">{{ itemName }}</li>
</template>

<script>
  export default {
    props: ['itemName']
  }
</script>
Run Example »

If we right click an <li> element in the browser we can see that the attributes have been merged. Margin is set directly on the <li> element inside the component, and is merged with the background-color that falls through from the parent:


$attrs

If we have more than one element on the root level of the component, it is no longer clear which element the attributes should fall through to.

To define which root element gets the fallthrough attributes we can mark the element with the built-in $attrs object, like this:

Example

TodoItem.vue:

<template>
  <div class="pinkBall"></div>
  <li v-bind="$attrs">{{ itemName }}</li>
  <div class="pinkBall"></div>
</template>
Run Example »

Styling defined inside the <style> tag in a component, or in App.vue, is actually available globally in all components.

To keep the styling limited locally to just the component, we can use the scope attribute on that component: <style scoped>

Global Styling

CSS written inside the <style> tag in any *.vue file works globally.

This means that if we for example set <p> tags to have pink background color inside the <style> tag in one *.vue file, this will affect <p> tags in all of the *.vue files in that project.

ExampleGet your own Vue Server

In this application we have three *.vue files: App.vue, and two components CompOne.vue and CompTwo.vue.

The CSS styling inside CompOne.vue affects <p> tags in all three *.vue files:

<template>
  <p>This p-tag belongs to 'CompOne.vue'</p>
</template>

<script></script>

<style>
  p {
    background-color: pink;
    width: 150px;
  }
</style>
Run Example »

Scoped Styling

To avoid that the styling in one component affects the styling of elements in other components we use the 'scoped' attribute on the <style> tag:

Example

The <style> tag in CompOne.vue is given the scoped attribute:

<template>
  <p>This p-tag belongs to 'CompOne.vue'</p>
</template>

<script></script>

<style scoped>
  p {
    background-color: pink;
    width: 150px;
  }
</style>
Run Example »

The way we have included components so far makes them accessible from all *.vue files in a project.

Components can be made to be local, meaning that they are only accessible inside a specific *.vue file.

Global Components

The way we have included components inside main.js so far make the components accessible inside the <template> of all other *.vue files in that project.

ExampleGet your own Vue Server

We use the CompOne.vue component inside both CompTwo.vue and App.vue to show that components are accessible to each other with our current main.js setup.

main.js:

import { createApp } from 'vue'
 
import App from './App.vue'
import CompOne from './components/CompOne.vue'
import CompTwo from './components/CompTwo.vue'

const app = createApp(App)
app.component('comp-one', CompOne)
app.component('comp-two', CompTwo)
app.mount('#app')
Run Example »

Local Components

We can include a component directly in the <script> tag in a *.vue file instead of including it in main.js.

If we include a component directly in a *.vue file, the component beomes accessible only locally in that file.

Example

To make CompOne.vue local to App.vue, and only accessible there, we remove it from main.js.

main.js:

import { createApp } from 'vue'
 
import App from './App.vue'
import CompOne from './components/CompOne.vue' 
import CompTwo from './components/CompTwo.vue'
 
const app = createApp(App)
app.component('comp-one', CompOne) 
app.component('comp-two', CompTwo)
app.mount('#app')

And include CompOne.vue directly in the <script> tag of App.vue instead.

App.vue:

<template>
  <h3>Local Component</h3>
  <p>The CompOne.vue component is a local component and can only be used inside App.vue.</p>
  <comp-one />
  <comp-two />
</template>

<script> 
  import CompOne from './components/CompOne.vue';

  export default {
    components: {
      'comp-one': CompOne
    }
  }
</script>
Run Example »

The CompOne.vue component is now only available in App.vue.

Slots are a powerful feature in Vue that allow for more flexible and reusable components.

We use slots in Vue to send content from the parent into the <template> of a child component.

Slots

So far we have just used components inside <template> as self-closing tags like this:

App.vue:

<template>
  <slot-comp />
</template>

Instead, we can use opening and closing tags, and put some content inside, like for example a text:

App.vue:

<template>
  <slot-comp>Hello World!</slot-comp>
</template>

But to receive 'Hello World!' inside the component and display it on our page, we need to use the <slot> tag inside the component. The <slot> tag acts as a placeholder for the content, so that after the application is built the <slot> will be replaced by the content sent to it.

ExampleGet your own Vue Server

SlotComp.vue:

<template>
  <div>  
    <p>SlotComp.vue</p>
    <slot></slot>
  </div>
</template>
Run Example »

Slots as Cards

Slots can also be used to wrap around larger chunks of dynamic html content to get a card-like appearance.

Earlier we have sent data as props to create content inside compoents, now we can just send the HTML content directly inside the <slot> tag as it is.

Example

App.vue:

<template>
  <h3>Slots in Vue</h3>  
  <p>We create card-like div boxes from the foods array.</p>
  <div id="wrapper">
    <slot-comp v-for="x in foods">
      <img v-bind:src="x.url">
      <h4>{{x.name}}</h4>
      <p>{{x.desc}}</p>
    </slot-comp>
  </div>
</template>

As the content enters the component where the <slot> is, we use a div around the <slot> and style the <div> locally to create a card-like appearance around the content without affecting other divs in our application.

SlotComp.vue:

<template>
  <div> <!-- This div makes the card-like appearance -->
    <slot></slot>
  </div>
</template>

<script></script>

<style scoped>
  div {
    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
    border-radius: 10px;
    margin: 10px;
  }
</style>
Run Example »

Components that produce a card-like frame around content can be reused to create different elements, but with the same card-like frame around.

In this example we use the same component as for the food items to create a footer.

Example

App.vue:

<template>
  <h3>Reusable Slot Cards</h3>
  <p>We create card-like div boxes from the foods array.</p>
  <p>We also create a card-like footer by reusing the same component.</p>
  <div id="wrapper">
    <slot-comp v-for="x in foods">
      <img v-bind:src="x.url">
      <h4>{{x.name}}</h4>
    </slot-comp>
  </div>
  <footer>
    <slot-comp>
      <h4>Footer</h4>
    </slot-comp>
  </footer>
</template>
Run Example »

Fallback Content

If a component is created without content we can have fallback content in the <slot>.

Example

The first component in this application has no content provided, so the fallback content is rendered.

App.vue:

<template>
  <h3>Slots Fallback Content</h3>
  <p>A component without content provided can have fallback content in the slot tag.</p>
  <slot-comp>
    <!-- Empty -->
  </slot-comp>
  <slot-comp>
    <h4>This content is provided from App.vue</h4>
  </slot-comp>
</template>

SlotComp.vue:

<template>
  <div>
    <slot>
      <h4>This is fallback content</h4>
    </slot>
  </div>
</template>
Run Example »

We need the v-slot directive to refer to named slots.

Named slots allow for more control over where the content is placed within the child component's template.

Named slots can be used to create more flexible and reusable components.

Before using v-slot and named slots, let's see what happens if we use two slots in the component:

ExampleGet your own Vue Server

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp>'Hello!'</slot-comp>

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot></slot>
</div>
<div>
  <slot></slot>
</div>
Run Example »

With two slots in a component, we can see that the content simply appears both places.


v-slot and Named Slots

If we have more than one <slot> in a component, but we want to control in which <slot> the content should appear, we need to name the slots and use v-slot to send the content to the right place.

Example

To be able to differentiate the slots we give the slots different names.

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot name="topSlot"></slot>
</div>
<div>
  <slot name="bottomSlot"></slot>
</div>

And now we can use v-slot in App.vue to direct the content to the right slot.

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp v-slot:bottomSlot>'Hello!'</slot-comp>
Run Example »

Default Slots

If you have a <slot> with no name, that <slot> will be default for components marked with v-slot:default, or components that are not marked with v-slot.

To see how this works we just need to make two small changes in our previous example:

Example

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot name="topSlot"></slot>
</div>
<div>
  <slot name="bottomSlot"></slot>
</div>

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp v-slot:bottomSlot>'Hello!'</slot-comp>
Run Example »

As already mentioned, we can mark content with the default value v-slot:default to make it even more clear that the content belongs to the default slot.

Example

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot></slot>
</div>
<div>
  <slot name="bottomSlot"></slot>
</div>

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp v-slot:default>'Default slot'</slot-comp>
Run Example »

v-slot in <template>

As you have seen the v-slot directive can be used as an attribute in the component tag.

v-slot can also be used in a <template> tag to direct larger parts of content to a certain <slot>.

Example

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp>
  <template v-slot:bottomSlot>
    <h4>To the bottom slot!</h4>
    <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
  </template>
  <p>This goes into the default slot</p>
</slot-comp>

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot></slot>
</div>
<div>
  <slot name="bottomSlot"></slot>
</div>
Run Example »

We use the <template> tag to direct some content to a certain <slot> because the <template> tag is not rendered, it is just a placeholder for the content. You can see this by inspecting the built page: you will not find the template tag there.


v-slot Shorthand #

The shorthand for v-slot: is #.

This means that:

<slot-comp v-slot:topSlot>'Hello!'</slot-comp>

Can be written as:

<slot-comp #topSlot>'Hello!'</slot-comp>

Example

App.vue:

<h1>App.vue</h1>
<p>The component has two div tags with one slot in each.</p>
<slot-comp #topSlot>'Hello!'</slot-comp>

SlotComp.vue:

<h3>Component</h3>
<div>
  <slot name="topSlot"></slot>
</div>
<div>
  <slot name="bottomSlot"></slot>
</div>
Run Example »

Scoped slot provides local data from the component so that the parent can choose how to render it.

Send Data to Parent

We use v-bind in the component slot to send local data to the parent:

SlotComp.vue:

<template>
  <slot v-bind:lclData="data"></slot>
</template>

<script>
  export default {
    data() {
      return {
        data: 'This is local data'
      }
    }
  }
</script>

The data inside the component can be referred to as 'local' because it is not accessible to the parent unless it is sent up to the parent like we do here with v-bind.


Receive Data from Scoped Slot

The local data in the component is sent with v-bind, and it can be received in the parent with v-slot:

ExampleGet your own Vue Server

App.vue:

<slot-comp v-slot:"dataFromSlot">
  <h2>{{ dataFromSlot.lclData }}</h2>
</slot-comp>
Run Example »

In the example above, 'dataFromSlot' is just a name we can choose ourselves to represent the data object we receive from the scoped slot. We get the text string from the slot by using the 'lclData' property, and we use interpolation to finally render the text in an <h2> tag.


Scoped Slot with an Array

A scoped slot can send data from an array by using v-for, but the code in App.vue is basically the same:

Example

SlotComp.vue:

<template>
  <slot
    v-for="x in foods"
    :key="x"
    :foodName="x"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        foods: ['Apple','Pizza','Rice','Fish','Cake']
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="food">
  <h2>{{ food.foodName }}</h2>
</slot-comp>
Run Example »

Scoped Slot with an Array of Objects

A scoped slot can send data from an array of objects by using v-for:

Example

SlotComp.vue:

<template>
  <slot
    v-for="x in foods"
    :key="x.name"
    :foodName="x.name"
    :foodDesc="x.desc"
    :foodUrl="x.url"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        foods: [
          { name: 'Apple', desc: 'Apples are a type of fruit that grow on trees.', url: 'img_apple.svg' },
          { name: 'Pizza', desc: 'Pizza has a bread base with tomato sauce, cheese, and toppings on top.', url: 'img_pizza.svg' },
          { name: 'Rice', desc: 'Rice is a type of grain that people like to eat.', url: 'img_rice.svg' },
          { name: 'Fish', desc: 'Fish is an animal that lives in water.', url: 'img_fish.svg' },
          { name: 'Cake', desc: 'Cake is something sweet that tastes good but is not considered healthy.', url: 'img_cake.svg' }
       ]
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="food">
  <hr>
  <h2>{{ food.foodName }}<img :src=food.foodUrl></h2>
  <p>{{ food.foodDesc }}</p>
</slot-comp>
Run Example »

Static Data from a Scoped Slot

A scoped slot can also send static data, that is data that does not belong to the data property of the Vue instance.

When sending static data we do not use v-bind.

In the example below we send one static text, and one text bound dynamically to the data instance so that we can see the difference.

Example

SlotComp.vue:

<template>
  <slot
    staticText="This text is static"
    :dynamicText="text"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        text: 'This text is from the data property'
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="texts">
  <h2>{{ texts.staticText }}</h2>
  <p>{{ texts.dynamicText }}</p>
</slot-comp>
Run Example »

Named Scoped Slots

Scoped slots can be named.

To use named scoped slots we need to name the slots inside the component with the 'name' attribute.

And to receive data from a named slot we need to refer to that name in the parent where we use the component, with the v-slot directive, or shorthand #.

Example

In this example the component is created one time referring to the slot "leftSlot", and one time referring to the slot "rightSlot".

SlotComp.vue:

<template>
  <slot
    name="leftSlot"
    :text="leftText"
  ></slot>
  <slot
    name="rightSlot"
    :text="rightText"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        leftText: 'This text belongs to the LEFT slot.',
        rightText: 'This text belongs to the RIGHT slot.'
      }
    }
  }
</script>

App.vue:

<slot-comp #leftSlot="leftProps">
  <div>{{ leftProps.text }}</div>
</slot-comp>
<slot-comp #rightSlot="rightProps">
  <div>{{ rightProps.text }}</div>
</slot-comp>
Run Example »

Alternatively, we can create the component one time, with two different "template" tags, each "template" tag referring to a different slot.

Example

In this example the component is created only one time, but with two "template" tags, each referring to a different slot.

SlotComp.vue is exactly the same as in the previous example.

App.vue:

<slot-comp>

  <template #leftSlot="leftProps">
    <div>{{ leftProps.text }}</div>
  </template>

  <template #rightSlot="rightProps">
    <div>{{ rightProps.text }}</div>
  </template>

</slot-comp>
Run Example »

Dynamic Components can be used to flip through pages within your page, like tabs in your browser, with the use of the 'is' attribute.

The Component Tag and The 'is' Attribute

To make a dynamic component we use the <component> tag to represent the active component. The 'is' attribute is tied to a value with v-bind, and we change that value to the name of the component we want to have active.

ExampleGet your own Vue Server

In this example we have a <component> tag that acts as a placeholder for either the comp-one component or the comp-two component. The 'is' attribute is set on the <component> tag and listens to the computed value 'activeComp' that holds either 'comp-one' or 'comp-two' as value. And we have a button that toggles a data property between 'true' and 'false' to make the computed value switch between the active components.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <p>App.vue switches between which component to show.</p>
  <button @click="toggleValue = !toggleValue">
    Switch component
  </button>
  <component :is="activeComp"></component>
</template>

<script>
  export default {
    data() {
      return {
        toggleValue: true
      }
    },
    computed: {
      activeComp() {
        if(this.toggleValue) {
          return 'comp-one'
        }
        else {
          return 'comp-two'
        }
      }
    }
  }
</script>
Run Example »

<KeepAlive>

Run the example below. You will notice that changes you make in one component is forgotten when you switch back to it. That is because the component is unmounted and mounted again, reloading the component.

Example

This example is the same as the previous example except the components are different. In comp-one you can choose between 'Apple' and 'Cake', and in comp-two you can write a message. Your inputs will be gone when you return to a component.

Run Example »

To keep the state, your previous inputs, when returning to a component we use the <KeepAlive> tag around the <component> tag.

Example

The components now remember the user inputs.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <p>App.vue switches between which component to show.</p>
  <button @click="toggleValue = !toggleValue">
    Switch component
  </button>
  <KeepAlive>
    <component :is="activeComp"></component>
  </KeepAlive>
</template>
Run Example »

The 'include' and 'exclude' Attributes

All components inside the <KeepAlive> tag will be kept alive by default.

But we can also define only some components to be kept alive by using 'include' or 'exclude' attributes on the <KeepAlive> tag.

If we use the 'include' or 'exclude' attributes on the <KeepAlive> tag we also need to give the components names with the 'name' option:

CompOne.vue:

<script>
  export default {
    name: 'CompOne',
    data() {
      return {
        imgSrc: 'img_question.svg'
      }
    }
  }
</script>

Example

With <KeepAlive include="CompOne">, only the 'CompOne' component will remember its state, the previous inputs.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <p>App.vue switches between which component to show.</p>
  <button @click="toggleValue = !toggleValue">
    Switch component
  </button>
  <KeepAlive include="CompOne">
    <component :is="activeComp"></component>
  </KeepAlive>
</template>
Run Example »

We can also use 'exclude' to choose which components to keep alive or not.

Example

With <KeepAlive exclude="CompOne">, only the 'CompTwo' component will remember its state.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <p>App.vue switches between which component to show.</p>
  <button @click="toggleValue = !toggleValue">
    Switch component
  </button>
  <KeepAlive exclude="CompOne">
    <component :is="activeComp"></component>
  </KeepAlive>
</template>
Run Example »

Both 'include' and 'exclude' can be used with multiple components by using comma separation.

To show this we will add one more component so that we get three components in total.

Example

With <KeepAlive include="CompOne, CompThree">, both the 'CompOne' and the 'CompThree' components will remember their state.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <button @click="compNbr++">
    Next component
  </button>
  <KeepAlive include="CompOne,CompThree">
    <component :is="activeComp"></component>
  </KeepAlive>
</template>
Run Example »

The 'max' Attribute

We can use 'max' as an attribute to the <KeepAlive> tag to limit the number of components the browser needs to remember the state of.

Example

With <KeepAlive :max="2">, the browser will only remember the user input of the last two visited components.

App.vue:

<template>
  <h1>Dynamic Components</h1>
  <label><input type="radio" name="rbgComp" v-model="compName" :value="'comp-one'"> One</label>
  <label><input type="radio" name="rbgComp" v-model="compName" :value="'comp-two'"> Two</label>
  <label><input type="radio" name="rbgComp" v-model="compName" :value="'comp-three'"> Three</label>
  <KeepAlive :max="2">
    <component :is="activeComp"></component>
  </KeepAlive>
</template>

The Vue <Teleport> tag is used to move content to a different place in the DOM structure.

<Teleport> and The 'to' Attribute

To move some content to somewhere else in the DOM structure we use the Vue <Teleport> tag around the content and the 'to' attribute to define where to move it.

<Teleport to="body">
  <p>Hello!</p>
</Teleport>

The 'to' attribute value is given as CSS notation, so if we want to send some content to the body tag like in the code above we simply write <Teleport to="body">.

We can see that the content is moved to the body tag by inspecting the page after it has loaded.

ExampleGet your own Vue Server

CompOne.vue:

<template>
  <div>
    <h2>Component</h2>
    <p>This is the inside of the component.</p>
    <Teleport to="body">
      <div id="redDiv">Hello!</div>
    </Teleport>
  </div>
</template>
Run Example »

If we right-click our page and choose 'Inspect', we can see that the red <div> element is moved out of the component and to the end of the <body> tag.

We could also for example have a tag with an id <div id="receivingDiv"> and teleport some content to that <div> by using <Teleport to="#receivingDiv"> around the content we want to teleport/move.


Script and Style of Teleported Elements

Even though some content is moved out of a component with the <Teleport> tag, relevant code inside the component in the <script> and <style> tags still works for the moved content.

Example

Relevant code from the <style> and <script> tags still works for the teleported <div> tag even though it is no longer inside the component after compilation.

CompOne.vue:

<template>
  <div>
    <h2>Component</h2>
    <p>This is the inside of the component.</p>
    <Teleport to="body">
      <div 
        id="redDiv" 
        @click="toggleVal = !toggleVal" 
        :style="{ backgroundColor: bgColor }"
      >
        Hello!<br>
        Click me!
      </div>
    </Teleport>
  </div>
</template>

<script>
export default {
  data() {
    return {
      toggleVal: true
    }
  },
  computed: {
    bgColor() {
      if (this.toggleVal) {
        return 'lightpink'
      }
      else {
        return 'lightgreen'
      }
    }
  }
}
</script>

<style scoped>
#redDiv {
  margin: 10px;
  padding: 10px;
  display: inline-block;
}

#redDiv:hover {
  cursor: pointer;
}
</style>
Run Example »

The HTTP request is a part of the communication between a client and a server.

The client sends an HTTP request to the server, which handles the request and returns an HTTP response.

HTTP

HTTP stands for Hyper Text Transfer Protocol.

Our browser makes HTTP requests all the time in the background when we browse the Internet. When we access an Internet page, our browser (the client) sends several HTTP requests to make the server send us the page we want with all the relevant files and data as HTTP responses.

The most common kinds of HTTP requests are POSTGETPUTPATCH, and DELETE. Learn more about the different kinds of HTTP requests on our HTTP Request Methods page.

Learn more about what HTTP is on our What is HTTP page.


The 'fetch' Method

To get data from a server in Vue we can use the JavaScript fetch() method.

When we use the fetch() method in this tutorial we will not specify the HTTP request method, and that means that the default request method GET is what is used in the background.

The fetch() method expects a URL address as an argument so that it knows where to get the data from.

Here is a simple example that uses the fetch() method to send an HTTP GET request, and receive data as an HTTP response. The data requested in this case is the text inside the local file file.txt:

ExampleGet your own Vue Server

App.vue:

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <p v-if="data">{{ data }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: null,
    };
  },
  methods: {
    fetchData() {
      const response = fetch("file.txt");
      this.data = response;
    }
  }
};
</script>
Run Example »

In the example above, we only get "[object Promise]" as a result, but that is not what we want.

We get this result because fetch() is a promised-based method that returns a promise object. The first return the fetch() method gives is therefore just an object which means that the HTTP request has been sent. This is the "pending" state.

When the fetch() method actually gets the data we want, the promise is fulfilled.

To wait for the response to be fulfilled, with the data we want, we need to use the await operator in front of the fetch() method:

const response = await fetch("file.txt");

When the await operator is used inside a method, the method is required to be declared with the async operator:

async fetchData() {
  const response = await fetch("file.txt");
  this.data = response;
}

The async operator tells the browser that the method is asynchronous, which means that it waits for something, and the browser can continue to do other tasks while it waits for the method to complete.

Now what we get is a "Response", and no longer just a "Promise", which means we are one step closer to get the actual text inside the file.txt file:

Example

App.vue:

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <p v-if="data">{{ data }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: null,
    };
  },
  methods: {
    async fetchData() {
      const response = await fetch("file.txt");
      this.data = response;
    }
  }
};
</script>
Run Example »

To get the text inside the file.txt file we need to use the text() method on the response. Because the text() method is a promise based method, we need to use the await operator in front of it.

Finally! We now have what we need to get the text from inside the file.txt file with the fetch() method:

Example

App.vue:

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <p v-if="data">{{ data }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: null,
    };
  },
  methods: {
    async fetchData() {
      const response = await fetch("file.txt");
      this.data = await response.text();
    }
  }
};
</script>
Run Example »

Fetch Data from a JSON File

In the previous example we fetched text from a .txt file. But there are many ways to store data, and now we will see how we can fetch information from a .json file.

JSON is a common file format that is easy to work with because data is stored as text so that it is easy to read for humans, and the JSON format is also widely supported by programming languages, so that we can, for example, specify what data to extract from a .json file.

To read data from a .json file, the only change we need to do to the example above is to fetch a .json file, and use the json() method instead of the text() method on the response.

The json() method reads the response from the HTTP request and returns a JavaScript object.

We use the <pre> tag here to show the JSON formatted text because it preserves spaces and line breaks so that it is easier to read.

Example

App.vue:

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <pre v-if="data">{{ data }}</pre>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: null,
    };
  },
  methods: {
    async fetchData() {
      const response = await fetch("bigLandMammals.json");
      this.data = await response.json();
    }
  }
};
</script>
Run Example »

Because the result of the json() method is a JavaScript object, we can modify the example above to show a random animal from the bigLandMammals.json file:

Example

App.vue:

<template>
  <p>Try clicking the button more than once to see new animals picked randomly.</p>
  <button @click="fetchData">Fetch Data</button>
  <div v-if="randomMammal">
    <h2>{{ randomMammal.name }}</h2>
    <p>Max weight: {{ randomMammal.maxWeight }} kg</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      randomMammal: null
    };
  },
  methods: {
    async fetchData() {
      const response = await fetch("bigLandMammals.json");
      const data = await response.json();
      const randIndex = Math.floor(Math.random()*data.results.length);
      this.randomMammal = data.results[randIndex];
    }
  }
};
</script>
Run Example »

Data from an API

API stands for Application Programming Interface. You can learn more about API here.

There are a lot of interesting free APIs we can connect with and use, to get weather data, stock exchange data, etc.

The response we get when we call an API with an HTTP request can contain all kinds of data, but often contains data in the JSON format.

Example

A button can be clicked to get a random user from the random-data-api.com API.

App.vue:

<template>
  <h1>Example</h1>
  <p>Click the button to fetch data with an HTTP request.</p>
  <p>Each click generates an object with a random user from <a href="https://random-data-api.com/" target="_blank">https://random-data-api.com/</a>.</p>
  <p>The robot avatars are lovingly delivered by <a href="http://Robohash.org" target="_blank">RoboHash</a>.</p>
  <button @click="fetchData">Fetch data</button>
  <pre v-if="data">{{ data }}</pre>
</template>

<script>
  export default {
    data() {
      return {
        data: null,
      };
    },
    methods: {
      async fetchData() {      
        const response = await fetch("https://random-data-api.com/api/v2/users"); 
        this.data = await response.json();
      }   
    }
  };
</script>
Run Example »

We can modify our previous example a little bit to include the random user in a more user friendly way:

Example

We show the random user name in a <pre> tag, along with the job title and image when the button is clicked.

App.vue:

<template>
  <h1>Example</h1>
  <p>Click the button to fetch data with an HTTP request.</p>
  <p>Each click generates an object with a random user from <a href="https://random-data-api.com/" target="_blank">https://random-data-api.com/</a>.</p>
  <p>The robot avatars are lovingly delivered by <a href="http://Robohash.org" target="_blank">RoboHash</a>.</p>
  <button @click="fetchData">Fetch data</button>
  <div v-if="data" id="dataDiv">
    <img :src="data.avatar" alt="avatar">
    <pre>{{ data.first_name + " " + data.last_name }}</pre>
    <p>"{{ data.employment.title }}"</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        data: null,
      };
    },
    methods: {
      async fetchData() {      
        const response = await fetch("https://random-data-api.com/api/v2/users"); 
        this.data = await response.json();
      },    
    }
  };
</script>

<style>
#dataDiv {
  width: 240px;
  background-color: aquamarine;
  border: solid black 1px;
  margin-top: 10px;
  padding: 10px;
}
#dataDiv > img {
  width: 100%;
}
pre {
  font-size: larger;
  font-weight: bold;
}
</style>
Run Example »

HTTP Request in Vue with The 'axios' Library

The 'axios' JavaScript library also allows us to make HTTP requests.

To create and run the example on your own machine you first need to install the 'axios' library using the terminal in your project folder, like this:

npm install axios

This is how we can use the 'axios' library in Vue to fetch a random user:

Example

Only small changes are made to the previous example to do the HTTP request with the 'axios' library instead.

App.vue:

<template>
  <h1>Example</h1>
  <p>Click the button to fetch data with an HTTP request.</p>
  <p>Each click generates an object with a random user from <a href="https://random-data-api.com/" target="_blank">https://random-data-api.com/</a>.</p>
  <p>The robot avatars are lovingly delivered by <a href="http://Robohash.org" target="_blank">RoboHash</a>.</p>
  <button @click="fetchData">Fetch data</button>
  <div v-if="data" id="dataDiv">
    <img :src="data.data.avatar" alt="avatar">
    <pre>{{ data.data.first_name + " " + data.data.last_name }}</pre>
    <p>"{{ data.data.employment.title }}"</p>
  </div>
</template>

<script>
  import axios from 'axios'

  export default {
    data() {
      return {
        data: null,
      };
    },
    methods: {
      async fetchData() {      
        this.data = await axios.get("https://random-data-api.com/api/v2/users");
      }
    }
  };
</script>

<style>
#dataDiv {
  width: 240px;
  background-color: aquamarine;
  border: solid black 1px;
  margin-top: 10px;
  padding: 10px;
}
#dataDiv > img {
  width: 100%;
}
pre {
  font-size: larger;
  font-weight: bold;
}
</style>
Run Example »

Vue Template Refs are used to refer to specific DOM elements.

When the ref attribute is set on an HTML tag, the resulting DOM element is added to the $refs object.

We can use the ref attribute and the $refs object in Vue as an alternative to methods in plain JavaScript like getElementById() or querySelector().

The 'ref' Attribute and The '$refs' Object

HTML tags with the ref attribute will be added to the $refs object and can be reached later from inside the <script> tag.

ExampleGet your own Vue Server

The text inside a <p> element is changed.

App.vue:

<template>
  <h1>Example</h1>
  <p>Click the button to put "Hello!" as the text in the green p element.</p>
  <button @click="changeVal">Change Text</button>
  <p ref="pEl">This is the initial text</p>
</template>

<script>
  export default {
    methods: {
      changeVal() {
        this.$refs.pEl.innerHTML = "Hello!";
      }
    }
  }
</script>
Run Example »

Below is another example where the $refs object is used to copy the value of one tag into another tag.

Example

The text from the first <p> tag is copied into the second <p> tag.

App.vue:

<template>
  <h1>Example</h1>
  <p ref="p1">Click the button to copy this text into the paragraph below.</p>
  <button @click="transferText">Transfer text</button>
  <p ref="p2">...</p>
</template>

<script>
  export default {
    methods: {
      transferText() { 
        this.$refs.p2.innerHTML = this.$refs.p1.innerHTML;
      }
    }
  };
</script>
Run Example »

Get The Input Value from '$refs'

We can go further into an HTML element added to the $refs object to access any property we want.

Example

<p> element gets the same content as what's being written in the input field.

App.vue:

<template>
  <h1>Example</h1>
  <p>Start writing inside the input element, and the text will be copied into the last paragraph by the use of the '$refs' object.</p>
  <input ref="inputEl" @input="getRefs" placeholder="Write something..">
  <p ref="pEl"></p>
</template>

<script>
  export default {
    methods: {
      getRefs() { 
        this.$refs.pEl.innerHTML = this.$refs.inputEl.value;
      }
    }
  };
</script>
Run Example »

'ref' with v-for

HTML elements created with v-for, with the ref attribute, will be added to the $refs object as an array.

Example

The button reveals the the third list element stored as an array element inside the $refs object.

App.vue:

<template>
  <h1>Example</h1>
  <p>Click the button to reveal the 3rd list element stored as an array element in the $refs object.</p>
  <button @click="getValue">Get the 3rd list element</button><br>
  <ul>
    <li v-for="x in liTexts" ref="liEl">{{ x }}</li>
  </ul>
  <pre>{{ thirdEl }}</pre>
</template>

<script>
  export default {
    data() {
      return {
        thirdEl: ' ',
        liTexts: ['Apple','Banana','Kiwi','Tomato','Lichi']
      }
    },
    methods: {
      getValue() { 
        this.thirdEl = this.$refs.liEl[2].innerHTML;
        console.log("this.$refs.liEl = ",this.$refs.liEl);
      }
    }
  };
</script>

<style>
pre {
  background-color: lightgreen;
  display: inline-block;
}
</style>

Post a Comment

0 Comments

•Give The opportunity to your child with GreatToCode Kid's • Online Coding Classes for Your Kid • Introduce Your kid To the world's of coding
•Fuel You Career with our 100+ Hiring Partners, Advance Your Career in Tech with GreatToCode. •Join The Largest Tech and coding Community and Fast Forward Your career with GreatToCode. •10000+ Learner's+ 90 % placement Guarantee. • Learning Coding is Better with the GreatToCode community .
•Greattocode Kid's •GreatToCode Career •GreatToCode Interview •GreatToCode Professional •GreatToCode for schools •GreatToCode For colleges •GreatToCods For Businesses.
Are you ready to join the millions of people learning to code? GreatToCode Pass is your one-stop-shop to get access to 1000+ courses, top-notch support, and successful job placement. What are you waiting for? Sign up now and get your future in motion with GreatToCode Pass.