Using Conditional Class Bindings in Vue

It's sort of cool to add a class to a component.

But the real fun begins when you can conditionally bind classes — turning them on and off as you wish.

In this article we'll cover:

  • Using guard expressions to conditionally add a class in Vue 3 and Vue 2 (it works exactly the same)
  • How ternaries can be used to bind classes conditonally
  • Using the object syntax (this is the best method)
  • Doing all of this with many classes at once!

Guard Expressions

There is a cool trick using the logical && (AND) that allows us to conditionally apply a class:

<template>
<span
class="description"
:class="useTheme && theme"
>
This is how you add dynamic classes in Vue.
</span>
</template>

This is known as a guard expression.

When useTheme is true, it will set the class to whatever the value of theme is.

But how does it work?

Here we have the variable useTheme which is a boolean, and theme is the value of the theme class.

In Javascript, the && (AND) operator will short-circuit if the first value is false.

Since both values need to be true in order for the expression to be true, if the first is false there is no point in checking what the second one is, since we already know the expression evaluates to false.

So if useTheme is false, the expression evaluates to false and no dynamic class name is applied.

However, if useTheme is true, it will also evaluate theme, and the expression will evaluate to the value of theme. This will then apply the value of theme as a classname.

This method is a bit clunky, so let's look at a better solution.

Ternaries

We can do a similar trick with ternaries.

If you aren't familiar, a ternary is basically a short-hand for an if-else statement.

They look like this:

const result = expression ? ifTrue : ifFalse;

Sometimes though, we'll format them like this for readability:

const result = expression
? ifTrue
: ifFalse;

If expression evaluates to true, we get ifTrue. Otherwise we will get ifFalse.

Their main benefit is that they are concise, and count as only a single statement. This lets us use them inside of our templates.

Ternaries are useful if we want to decide between two different values inside of the template:

<template>
<span
class="description"
:class="darkMode ? 'dark-theme' : 'light-theme'"
>
This is how you add dynamic classes in Vue.
</span>
</template>

If darkMode is true, we apply dark-theme as our class name. Otherwise we choose light-theme.

Object Syntax

The previous example could be re-written to use the object syntax instead:

<template>
<span
class="description"
:class="{
'dark-theme': darkMode,
'light-theme': !darkMode
}"
>
This is how you add dynamic classes in Vue.
</span>
</template>

Any time one of the values in the object evaluates to true, we add the key to the classes. We can stuff as many class names in there as we want, which is great if we're using utility CSS like Tailwind:

QuizComponent.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- ... -->
<label
v-for="(answer, index) in question.answers"
:key="index"
class="rounded-md py-3 px-5 border transition-colors duration-150"
:class="{
'border-gray-300': !showResults && question.selected !== index,
'border-gray-500 bg-gray-100':
!showResults && question.selected === index,
'border-green-500':
showResults && question.correctAnswers.includes(index),
'bg-green-100':
showResults &&
question.selected === index &&
question.correctAnswers.includes(index),
'border-red-500 bg-red-100':
showResults &&
question.selected === index &&
!question.correctAnswers.includes(index),
'cursor-not-allowed': showResults,
'cursor-pointer hover:bg-gray-100 hover:border-gray-500':
!showResults,
}"
>
<!-- ... -->

In this snippet I pulled from my course platform for Clean Components Toolkit, you can see we can have lots of complex logic that adds all sorts of different classes based on different conditions.

This component renders a question in a quiz, and these classes are responsible for highlighting when an answer is selected, correct, incorrect, or if it's disabled because the quiz is finished.

Computed Properties

Sometimes the logic needed to decide what class name to apply is a little more complicated.

In these cases, it doesn't really fit that nicely inside of the template, and instead you want to put it into a computed prop:

QuizComponent.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<!-- ... -->
<label
v-for="(answer, index) in question.answers"
:key="index"
class="rounded-md py-3 px-5 border transition-colors duration-150"
:class="computedClasses"
>
<!-- ... -->
</template>
<script setup>
import { computed } from 'vue';
const computedClasses = computed(() => ({
'border-gray-300': !showResults && question.selected !== index,
'border-gray-500 bg-gray-100':
!showResults && question.selected === index,
'border-green-500':
showResults && question.correctAnswers.includes(index),
'bg-green-100':
showResults &&
question.selected === index &&
question.correctAnswers.includes(index),
'border-red-500 bg-red-100':
showResults &&
question.selected === index &&
!question.correctAnswers.includes(index),
'cursor-not-allowed': showResults,
'cursor-pointer hover:bg-gray-100 hover:border-gray-500':
!showResults,
}));
</script>

This is a nice way to get things out of the template if you need to, but in this case we're not actually simplifying our code, just rearranging it.

Applying Multiple Classes

There are lots of ways to add class names conditionally in Vue 2 and in Vue 3.

To apply more than one class at a time, you can:

  • Use the array syntax
  • Use the object syntax (not as useful and not covered in this article)
  • Use computed props

I cover these in more detail, as well as generating class names on the fly, in this article on dynamic class names.