Skip to the solution
EasyFrontend9 min read

How do you bind classes and styles dynamically?

748 views

The problem

Question presented to candidate: "A status badge needs a red background when there is an error, a bold font when it is active, and a font size that comes from a slider. Show me how you would bind the classes and styles in Vue, and tell me what happens when the badge is a child component and the parent also passes a class."

What a strong answer should cover:

  • :class accepts a string, an object ({ active: isActive }, key = class name, value = on or off) or an array that can mix both.
  • A static class attribute and a bound :class on the same element are merged, not overwritten.
  • :style accepts an object (camelCase or quoted kebab-case keys) or an array of objects that are merged left to right; units are your job.
  • Put non-trivial logic in a computed object rather than a long inline expression.
  • On a component, class and style from the parent fall through to the single root element and merge with its own; a multi-root component needs $attrs.class bound explicitly, otherwise Vue warns.
  • Under the hood the compiler wraps the bindings in normalizeClass / normalizeStyle and marks the element with the CLASS / STYLE patch flags.

Clarifying questions expected:

  • "Is the badge a native element or a child component, and does that component have one root element?"
  • "Are the colours design tokens (classes) or truly dynamic values (inline styles or CSS variables)?"

Code / implementation expected: Yes. A template using object and array syntax for both bindings, ideally driven by a computed property.

  • class
  • style

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 10 sections

Read the code

VueObject, array and component class/style bindings
<script setup>
import { ref, computed, defineComponent, h } from "vue";

const isActive = ref(true);
const hasError = ref(false);
const size = ref(16);

// Object syntax: each key is a class, each value decides whether it is on
const classObject = computed(() => ({
  active: isActive.value,
  "text-danger": hasError.value,
}));

// Style object: camelCase keys, numbers need units you add yourself
const styleObject = computed(() => ({
  fontSize: size.value + "px",
  color: hasError.value ? "red" : "green",
}));

// A child with one root element: class and style from the parent merge into it
const Badge = defineComponent({
  setup(_, { slots }) {
    return () => h("span", { class: "badge", style: { padding: "2px" } }, slots.default?.());
  },
});
</script>

<template>
  <p id="obj" class="static" :class="classObject">object syntax</p>
  <p id="arr" :class="[isActive ? 'active' : '', { error: hasError }, 'base']">array syntax</p>
  <p id="sty" :style="[styleObject, { fontWeight: 'bold' }]">style binding</p>
  <Badge id="badge" class="from-parent" :style="{ margin: '4px' }">badge</Badge>
  <button @click="isActive = !isActive">toggle active</button>
  <button @click="hasError = !hasError">toggle error</button>
  <button @click="size += 4">bigger</button>
</template>

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 9 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextEasy

How does v-model work on form inputs?

Continue