How to Dynamically Add a Class Name in Vue

Being able to add a dynamic class name to your component is really powerful.

It lets you write custom themes more easily, add classes based on the state of the component, and also write different variations of a component that rely on styling.

Adding a dynamic class name is as simple as adding the prop :class="classname" to your component. Whatever classname evaluates to will be the class name that is added to your component.

Of course, there is a lot more we can do here with dynamic classes in Vue.

We'll cover a lot of stuff in this article:

  • Using static and dynamic classes in Vue
  • How we can use regular Javascript expressions to calculate our class
  • The array syntax for dynamic class names
  • The object syntax (for more variety!)
  • Generating class names on the fly
  • How to use dynamic class names on custom components

Not only that, but at the end I'll show you a simple pattern for using computed refs to help clean up your templates. This will come in handy once you start using dynamic class names everywhere!

Static and Dynamic Classes

In Vue we can add both static and dynamic classes to our components.

Static classes are the boring ones that never change, and will always be present on your component. On the other hand, dynamic classes are the ones we can add and remove things change in our application.

If we wanted to add a static class, it's exactly the same as doing it in regular HTML:

<template>
<span class="description">
This is how you add static classes in Vue.
</span>
</template>

Dynamic classes are very similar, but we have to use Vue's special property syntax, v-bind, in order to bind a Javascript expression to our class:

<template>
<span v-bind:class="'description'">
This is how you add static classes in Vue.
</span>
</template>

You'll notice we had to add extra quotes around our dynamic class name.

This is because the v-bind syntax takes in whatever we pass as a Javascript value. Adding the quotes makes sure that Vue will treat it as a string.

Vue also has a shorthand syntax for v-bind:

<template>
<span :class="'description'">
This is how you add static classes in Vue.
</span>
</template>

What's really neat is that you can even have both static and dynamic classes on the same component.

This let's you have some static classes for the things you know won't change, like positioning and layout, and dynamic classes for your theme:

<template>
<span
class="description"
:class="theme"
>
This is how you add static classes in Vue.
</span>
</template>
<script setup>
import { ref } from 'vue';
const theme = ref('blue-theme');
</script>
<style scoped>
.blue-theme {
color: navy;
background: white;
}
</style>

In this case, theme is the variable that contains the classname we will apply (remember, it's treating it as Javascript there).

Conditional Class Names

Since v-bind will accept any Javascript expression, we can do some pretty cool things with it.

My favourite is to use ternaries inside of the template, which tends to be quite clean and readable.

But we won't get into that here.

There are many different ways to conditionally bind classes in Vue, and they're worth exploring.

Using the Array Syntax

If there are lots of different classes you want to add dynamically, you can use arrays or objects. Both are useful, but we'll cover arrays first.

Since we are just evaluating a javascript expression, you can combine the expressions we just learned with the array syntax:

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

What's going on here?

We are using the array to set two dynamic class names on this element.

The value of fontTheme is a classname that will change how our fonts look.

From our previous example, we can still switch between light and dark themes using our darkMode variable.

Using the Object Syntax

We can even use an object to define the list of dynamic classes, which gives us some more flexibility.

For any key/value pair where the value is true, it will apply the key as the classname.

Let's look at an example of the object syntax:

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

Our object contains two keys, dark-theme and light-theme. Similar to the logic we implemented before, we want to switch between these themes based on the value of darkMode.

When darkMode is true, it will apply dark-theme as a dynamic class name to our element. But light-theme won't be applied because !darkMode will evaluate to false.

The opposite happens when darkMode is set to false. We get light-theme as our dynamic classname instead of dark-theme.

It is common convention to use dashes — or hyphens — in CSS classnames. But in order to write the object keys with dashes in Javascript, we need to surround it in quotes to make it a string.

Now we've covered the basics of dynamically adding classes to Vue components.

How do we do this with our own custom components?

Using with Custom Components

Let's say that we have a custom component that we are using in our app:

<template>
<MovieList
:movies="movies"
:genre="genre"
/>
</template>

If we want to dynamically add a class that will change the theme, what would we do?

It's actually really simple.

We just add the :class property like before!

<template>
<MovieList
:movies="movies"
:genre="genre"
:class="darkMode ? 'dark-theme' : 'light-theme'"
/>
</template>

The reason this works is that Vue will set class on the root element of MovieList directly.

When you set props on a component, Vue will compare those props to what the component has specified in it's props section. If there is a match, it will pass it along as a normal prop. Otherwise, Vue will add it to the root DOM element.

Here, because MovieList didn't specify a class property, Vue knows that it should set it on the root element.

There are some even more advanced things we can do with dynamic class names though...

Generating Class Names on the Fly

We've seen a lot of different ways that we can dynamically add or remove class names.

But what about dynamically generating the class name itself?

Let's say you have a Button component, with 20 different CSS styles for all of your different types of buttons.

That's a lot of variation!

You probably don't want to spend your whole day writing out every single one, along with the logic to turn it on and off. This would also be a nasty mess when it comes time to update the list!

Instead, we'll dynamically generate the name of the class we want to apply.

A simple version of this you've already seen:

<template>
<span
class="description"
:class="theme"
>
This is how you add static classes in Vue.
</span>
</template>
<script setup>
import { ref } from 'vue';
const theme = ref('blue-theme');
</script>
<style scoped>
.blue-theme {
color: navy;
background: white;
}
</style>

We can set a variable to contain the string of whatever class name we want.

If we wanted to do this for our Button component, we could do something simple like this:

<template>
<button
@click="$emit('click')"
class="button"
:class="theme"
>
{{ text }}
</button>
</template>
<script setup>
withDefaults(
defineProps({
theme: String,
}),
{
// Set default prop value
theme: 'default',
}
);
</script>
<style scoped>
.default {
/* ... */
}
.primary {
/* ... */
}
.danger {
/* ... */
}
</style>

Now, whoever is using the Button component can simply set the theme prop to whatever theme they want to use.

If they don't set any, it will add the .default class.

If they set it to primary, it will add the .primary class.

Cleaning Things Up With Computed Refs

Eventually the expressions in our template will get too complicated, and it will start to get very messy and hard to understand.

Luckily, we have an easy solution for that.

If we convert our expressions to computed refs, we can move more of the logic out of the template and clean it up:

<template>
<MovieList
:movies="movies"
:genre="genre"
:class="theme"
/>
</template>
<script setup>
import { ref, computed } from 'vue';
const darkMode = useDarkMode();
const theme = computed(() => darkMode ? 'dark-theme' : 'light-theme');
</script>

Not only is this much easier to read, but it's also easier to add in new functionality and refactor in the future.

This pattern — moving things from the template into computed refs — works with all sorts of things as well. It's one of the best tricks for cleaning up your Vue components!