Vue.js 3 Conditional Rendering Tutorial

In this Vue tutorial we learn how to render things based on the result of a condition.

We cover the binding directives, nested elements and some issues that may arise.

Lesson Video

If you prefer to learn visually, you can watch this lesson in video format.

Lesson Project

If you want to follow along with the examples in this lesson, you will need an app generated by the Vue CLI . If you already have one from the previous lesson, you can use it instead.

What is Conditional Rendering

When building applications, you will often have to show or hide HTML elements based on a certain condition.

Vue gives us the following directives to make it easy to condionally render elements.

  • v-if
  • v-else-if
  • v-else
  • v-show

Conditional rendering directives accept Javascript expressions that return true or false as their value. Vue will then render, or not render, the element based on the output of the evaluation.

The v-if directive

The v-if directive is attached to an element in the template we want to conditionally render. It works similar to a regular Javascript if statement in that it evaluates a truthy condition. If the condition proves true, the element will be rendered.

Example: v-if
<element v-if="expression" />

As an example, we’ll check if a number data property is 0 and only render the element if it is.

Example: src/App.vue
<template>
  <p v-if="num === 0">The number is 0</p>
</template>

<script>
export default {
  data() {
    return {
      num: 0
    }
  }
}
</script>

Because the condition proves true, the paragraph element will show in the browser. If we change num to anything else, the expression becomes false and the paragraph doesn’t render.

It is important to note what happens to the element when the expression returns false. Vue doesn’t just hide it with CSS, it completely removes the code from the HTML.

If we open up the browser’s developer tools and inspect the HTML, the paragraph’s markup is completely gone.

Output:
<div id="app" data-v-app>
  <!--v-if-->
</div>

Vue adds a comment where the paragraph will be injected if the directive evaluates to true.

The v-else-if ladder directive

Any additional evaluations that the v-if directive doesn’t cover is expressed in the v-else-if ladder.

The v-else-if directive cannot stand on its own and must directly follow a v-if in a separate element.

Syntax: v-else-if
<element v-if="expression" />
<element v-else-if="expression" />
<element v-else-if="expression" />

To demonstrate, let’s add two checks to our previous example to determine whether the number is positive or negative.

Example: src/App.vue
<template>
  <p v-if="num === 0">The number is 0</p>
  <p v-else-if="num > 0">The number is positive</p>
  <p v-else-if="num < 0">The number is negative</p>
</template>

<script>
export default {
  data() {
    return {
      num: 1
    }
  }
}
</script>

You can try experimenting with different positive and negative values to see which of the paragraphs render.

A break in the conditional block

As mentioned before, elements with conditional directives must directly follow each other for Vue to consider them as a connected block.

To demonstrate, let’s add a paragraph between the v-if and v-else-if conditionals in our earlier example.

Example: src/App.vue
<template>
  <p v-if="num === 0">The number is 0</p>
  <p>Breaking element</p>
  <p v-else-if="num > 0">The number is positive</p>
  <p v-else-if="num < 0">The number is negative</p>
</template>

<script>
export default {
  data() {
    return {
      num: 1
    }
  }
}
</script>

Vue won’t allow us to save the example above. Instead, it will raise the following error.

Output:
C:\VueProjects\my-app\src\App.vue
  4:6  error  'v-else-if' directives require being preceded
       by the element which has a 'v-if' or 'v-else-if' directive
       vue/valid-v-else-if

VueCompilerError: v-else/v-else-if has no adjacent v-if.
at C:\VueProjects\my-app\src\App.vue:4:3
2  |    <p v-if="num === 0">The number is 0</p>
3  |    <p>Breaking element</p>
4  |    <p v-else-if="num > 0">The number is positive</p>
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5  |    <p v-else-if="num < 0">The number is negative</p>
6  |  </template>

The error tells us that the underlined v-else-if doesn’t have an adjacent v-if .

The v-else fallback directive

The v-else directive is a catch-all fallback for any expressions that are not covered by v-if or v-else-if .

It doesn’t have a value and cannot stand on its own. It must follow either a v-if directive or a v-else-if ladder.

Syntax: v-else
<element v-if="expression" />
<element v-else />

To demonstrate, let’s add a check to our earlier example to see if the number is actually a number. We don’t have to do a data type check, we can just use the v-else to catch anything that’s not a number.

Example: src/App.vue
<template>
  <p v-if="num === 0">The number is 0</p>
  <p v-else-if="num > 0">The number is positive</p>
  <p v-else-if="num < 0">The number is negative</p>
  <p v-else>The number is a lie</p>
</template>

<script>
export default {
  data() {
    return {
      num: 'not-a-number'
    }
  }
}
</script>

Nested template element

Sometimes, we will need to display multiple elements from the result of one condition. But conditional directives only cover the element they are attached to.

As an example, let’s say we want to display both a heading and a paragraph when a boolean data property is true. We’ll have to add a v-if to both the elements.

Example: src/App.vue
<template>
  <h2 v-if="displayNotice">Notice</h2>
  <p  v-if="displayNotice">
    Thank you for noticing this notice
  </p>
</template>

<script>
export default {
  data() {
    return {
      displayNotice: true
    }
  }
}
</script>

This works fine and is a valid approach, but it’s not very efficient. We are repeating the same code more than once, which makes the application harder to test and debug.

One solution could be to wrap both elements in a div tag and attach the v-if to it instead of the heading and paragraph.

Example: src/App.vue
<template>
  <div v-if="displayNotice">
    <h2>Notice</h2>
    <p>Thank you for noticing this notice</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      displayNotice: true
    }
  }
}
</script>

This approach is also valid but it does create a few new problems.

  • It adds unnecessary code.
  • It Increases the DOM depth.
  • It may break our styling.

So, Vue allows us to use the template tag that defines the template block as a wrapper for other elements. Vue never renders the template tag, it’s only used to group elements.

To demonstrate, let’s change the wrapper in our example from a div to a template tag.

Example: src/App.vue
<template>
  <template v-if="displayNotice">
    <h2>Notice</h2>
    <p>Thank you for noticing this notice</p>
  </template>
</template>

<script>
export default {
  data() {
    return {
      displayNotice: true
    };
  }
}
</script>

Now we have the best of both worlds. We can conditionally render multiple elements without worrying about breaking the style or adding unnecessary depth to the DOM.

To confirm that the template tag isn’t rendered, inspect the elements in the browser’s developer tools. The source code should like similar to the following.

Output:
<div id="app" data-v-app="">
  <h2>Notice</h2>
  <p>Thank you for noticing this notice</p>
</div>