Multiple CSS classes in React

After learning how to import CSS files in React and style your components, probably the most common question which springs to mind is: How can I deal with multiple CSS classes in React? Or even something a bit more complex, such as: How can I apply one or more CSS classes conditionally to a given element in React? Well, this article will answer these questions, so, don’t blink, because it gets straight to the point! Have a good read.

Defining multiple classes statically

If you just want to apply multiple classes to some element and this class set never changes, there’s no need to complicate things:

const App = () => (
  <button className="btn btn--large btn--main" />
);

As you can see, the className attribute usage is pretty much identical to how we do in pure HTML, by using the class attribute. In simple words, a string with a list of classes separated by spaces. And that’s it.

Defining multiples classes dynamically

The previous example illustrates a simple scenario, where the applied class list doesn’t change, independently of the application’s state. But what if you wanted that one or more classes were applied based on certain condition? For instance, let’s say you have a component whose wrapper div should be styled with a given class if the loading state variable is set to true. Or if you have a three-step wizard and want to set a specific class depending on which step is active. There are multiple techniques to get this result in React. Let’s take a look at some of them.

Technique #1: Template literals

The first way to implement dynamic CSS classes in React is by using ES2015 template literals:

class Post extends Component {
  state = { loading: false };

  render() {
    return (
      <div className={`post-wrapper ${this.state.loading ? 'post-wrapper--loading' : ''}`}></div>
    );
  }
}

As you can see, this technique relies on ternary operator–based expressions.

To make it easier to see if it works properly, add the following piece of code to your component:

componentDidMount() {
  setInterval(() => this.setState({ loading: !this.state.loading }), 3000);
}

The above snippet toggles the loading state variable’s value each 3 seconds.

Now, let’s use the React Developer Tools extension to visualize what happens under the hood:


That’s cool! The value of the className attribute is dynamically updated every time the loading state variable changes.

Technique #2: The classList helper function

The previous technique works perfectly and is easy to read in simpler scenarios. However, when you’re dealing with a bigger class set, it can become a bit hard to understand at first glance:

class Wizard extends Component {
  state = { step: 0 };

  render() {
    const step = this.state.step;

    return (
      <div className={`
        wizard__step
        ${step === 0 ? 'wizard__step--personal-data' : ''}
        ${step === 1 ? 'wizard__step--shipping-address' : ''}
        ${step === 2 ? 'wizard__step--payment' : ''}
      `}></div>
    );
  }
}

For these cases, there are better alternatives. One of them consists in adding a simple utility function to your application, which handles the toggling logic for our set of classes. Before analyzing the function itself, let’s see its usage:

class Wizard extends Component {
  state = { step: 0 };

  render() {
    const step = this.state.step;

    return (
      <div className={classList({
        'wizard__step': true,
        'wizard__step--personal-data': step === 0,
        'wizard__step--shipping-address': step === 1,
        'wizard__step--payment': step === 2
      })}></div>
    );
  }
}

No ternary operators, no string interpolation, no empty strings for each class… I think you’ll agree it’s clearer than the previous version. =)

Now, here’s this classList function:

function classList(classes) {
  return Object
    .entries(classes)
    .filter(entry => entry[1])
    .map(entry => entry[0])
    .join(' ');
}

Simple, isn’t it? Its logic is equally easy to understand:

  1. First, it transforms our input object in an array of key-value pairs (one pair for each property).
  2. Then, it filters the resulting array to eliminate items whose toggling criteria evaluate to false.
  3. After that, it maps each item of the filtered array to its respective class name, outputting a new array with a list of classes.
  4. Last, but not least, it joins all classes using a space as separator. The function returns the resulting string.

Technique #3: classList function V2

You may have noticed that the example used to illustrate the previous technique has a class which is fixed: wizard__step. This is not only possible, but very common. Combining classes which should always be rendered with others that render according to certain criteria.

The classList function has a little drawback: it forces us to define a boolean value for fixed classes, like this:

classList({ 'wizard__step': true, [...] })

If this bothers you in some way, here’s another version of the classList utility function which simplifies the definition of fixed classes:

function classList(...classes) {
  return classes
    .filter(item => !!item)
    .join(' ');
}

Its logic is even simpler:

  1. By using rest parameters, it transforms the arguments list in an array.
  2. Then, it filters the resulting array, removing every entry which evaluates to false.
  3. Finally, it joins the remaining items using a space to separate them and returns this string. That’s it!

This new approach has a totally different usage:

class Wizard extends Component {
  state = { step: 0 };

  render() {
    const step = this.state.step;

    return (
      <div className={classList(
        'wizard__step',
        step === 0 && 'wizard__step--personal-data',
        step === 1 && 'wizard__step--shipping-address',
        step === 2 && 'wizard__step--payment'
      )}></div>
    );
  }
}

By filtering falsy values, this function allow us to use simpler expressions, without those ternary operators that we saw in the Technique #1. Besides, if you want to define a fixed class, just include its name in the arguments list. Simple like that!

Technique #4: The classNames package

The last approach we’ll see is to use classNames, a third-party package that consists of a simple function (much like classList). Its biggest advantage is to support multiple usages, like:

classNames('app', 'app--active')
classNames('app', { 'app--loading': true })
classNames('app', { 'app--loading': false, 'app--offline': true })
classNames('app', { 'app--loading': false }, { 'app--offline': true })
classNames('app', { 'app--loading': false }, 'app--active', { 'app--offline': false })
classNames('app', this.state.loading && 'app--loading')

If you need this type of flexibility, go ahead and install it:

npm install classnames –save

Or, for Yarn-lovers:

yarn add classnames

And here’s our wizard example rewritten to use classNames:

[...]
import classNames from 'classnames';

[...]
  render() {
    const step = this.state.step;

    return (
      <div className={classNames(
        'wizard__step',
        {
          'wizard__step--personal-data': step === 0,
          'wizard__step--shipping-address': step === 1,
          'wizard__step--payment': step === 2
        }
      )}></div>
    );
  }
[...]

Conclusion

  • Defining multiple CSS classes statically in JSX is identical to how it’s done in pure HTML
  • When it comes to defining classes dynamically, there are multiple possible ways.
  • The classNames package is a popular and versatile approach to handle multiple classes dynamically.

Pin It on Pinterest