Skip to content

The classMap() Directive

classMap() lets you toggle several CSS classes on an element declaratively, based on an object mapping class names to boolean conditions. This lesson covers its usage and common patterns.

Toggling Multiple Classes with One Expression

Without classMap, toggling several classes conditionally means manually building a class string yourself — string concatenation, template literals, or array joins, all a bit noisy for something this common. classMap({ className: condition, ... }) expresses the same intent declaratively: each key is a class name, and its boolean value controls whether that class is present.

classMap must be used specifically on the class attribute, and it works by efficiently adding/removing only the classes whose condition actually changed between renders, rather than replacing the entire className string on every update.

import { html } from 'lit';
import { classMap } from 'lit/directives/class-map.js';

render() {
  const classes = {
    card: true,
    'card--active': this.isActive,
    'card--disabled': this.isDisabled,
  };
  return html`<div class=${classMap(classes)}>...</div>`;
}

The base card class is always present; card--active and card--disabled toggle independently based on their own boolean conditions.

classMap() Signature

classMap({
  'class-name': booleanCondition,
  'another-class': anotherCondition,
})

// Must be used on the class attribute specifically:
html`<div class=${classMap({ ... })}></div>`;
  • Only classes with a truthy value in the object are applied; falsy ones are omitted (or removed if previously present).
  • classMap must be the *entire* value of the class attribute — it can't be mixed with additional static text in the same binding.
  • For a class that's always present regardless of state, include it in the object with a value of true rather than as separate static text.
  • classMap is imported from lit/directives/class-map.js.

classMap Cheat Sheet

Common patterns for conditional classes.

Pattern Example
Always-present base class { card: true }
Toggle based on a boolean property { 'is-active': this.active }
Toggle based on a computed condition { 'has-error': this.errors.length > 0 }
Multiple independent toggles { open: this.open, disabled: this.disabled }
BEM-style modifier classes { 'btn--primary': this.variant === 'primary' }

Combining a Static Base Class with Dynamic Ones

Since classMap must own the entire class attribute value, include any class that's always present as an entry with the value true, rather than trying to concatenate it separately alongside the directive.

// Correct:
html`<div class=${classMap({ card: true, 'card--active': this.active })}></div>`;

// Incorrect — can't mix static text with classMap in the same attribute:
html`<div class="card ${classMap({ 'card--active': this.active })}"></div>`;

The second example is invalid usage — classMap needs to fully own the class attribute's value.

Why Not Just Build a Class String Manually?

You certainly can compute a class string manually with a template literal or array .filter().join(' '), and for a couple of simple cases that's perfectly reasonable. classMap's advantage grows with the number of conditions — it stays readable as an object literal, and its diffing only touches the specific classes that actually changed, rather than replacing the whole className string on every render.

Common Mistakes

  • Mixing static text with classMap in the same class attribute binding, which isn't supported.
  • Using classMap on an attribute other than class — it's specifically designed for that one use case.
  • Building a manual class string when classMap would be clearer for a component with more than one or two conditional classes.
  • Forgetting to include always-present base classes as true-valued entries in the object.

Key Takeaways

  • classMap({ name: condition, ... }) declaratively toggles multiple CSS classes based on boolean conditions.
  • It must be the entire value of the class attribute — not combined with additional static text in the same binding.
  • Always-present classes should be included as true-valued entries in the object, not as separate static text.
  • It's imported from lit/directives/class-map.js and works specifically for the class attribute.

Pro Tip

Reach for classMap as soon as a component has two or more conditional classes — below that, a simple ternary on the class attribute is fine, but classMap scales far more readably as conditions are added later.