51

I'm using the following code to dynamically set a className in a React component based upon a boolean from props:

<div className={this.props.menuOpen ? 'inactive' : 'active'}>
...
</div>

However, I'm also using CSS Modules, so now I need to set the className to:

import styles from './styles.css';

<div className={styles.sideMenu}>
...
</div>

I'm having trouble with this - I tried using classnames to gain more control with multiple classes, but because I need the end result to be that the className is set to both styles.sideMenu AND styles.active (in order for CSS Modules to kick in) I'm unsure how to handle this.

Any guidance is greatly appreciated.

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
Toby
  • 12,743
  • 8
  • 43
  • 75
  • 1
    Why wouldn't `classnames` work for you? This is the case it's designed for. – Chris Jul 14 '16 at 19:28
  • I got this far with `classnames`: ```let classNames = classnames(styles.sideMenu, { active: this.props.menuOpen, }); ``` However, the key `active` must be `styles.active` (so that I can place my css in the imported stylesheet), and setting to this creates an error. Perhaps I'm misunderstanding the documentation? – Toby Jul 14 '16 at 19:37

11 Answers11

81

Using classnames and es6:

let classNames = classnames(styles.sideMenu, { [styles.active]: this.props.menuOpen });

Using classnames and es5:

var classNames = classnames(styles.sideMenu, this.props.menuOpen ? styles.active : '');
mixel
  • 25,177
  • 13
  • 126
  • 165
Chris
  • 17,119
  • 5
  • 57
  • 60
  • { [styles.active]: this.props.menuOpen } what is the syntax? i can't find it in es6 – FroDK Dec 29 '21 at 20:24
  • 1
    @FroDK They're called computed property names. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#new_notations_in_ecmascript_2015 – Chris Jan 04 '22 at 16:44
21

Bit late to the party here, but using string templates works for me - you could move the ternary operator out to a const if you'd like as well:

<div className={`${styles.sideMenu} ${this.props.menuOpen ? styles.inactive : styles.active}`>
...
</div>
bertybro
  • 600
  • 5
  • 5
6

I wanted to just add on a better way of using the bind api of classnames npm. You can bind the classnames to the styles object imported from css like below:

import classNames from 'classnames/bind';
import styles from './index.css';

let cx = classNames.bind(styles);

and use it like this:

cx("sideMenu", "active": isOpen)

where sideMenu and active are in styles object.

orangespark
  • 631
  • 6
  • 19
3

Using logical AND instead of ternary operator makes it even less verbose since classnames omits a falsy value.

<div className={ classNames(styles.sideMenu, this.props.menuOpen && styles.active) }></div>
rexblack
  • 151
  • 1
  • 2
2

This is the closest I can get to a working solution:

const isActive = this.props.menuOpen ? styles.inactive : styles.active;

<div className={isActive + ' ' + styles.sideMenu}>

This does work - both allow the styles in the imported stylesheet to be used, and is only applied when this.props.menuOpen is true.

However, it's pretty hacky - I'd love to see a better solution if anyone has any ideas.

Toby
  • 12,743
  • 8
  • 43
  • 75
2

Using Array.join

<div className={[styles.sideMenu, this.props.menuOpen ? styles.show : styles.hide].join(' ')}></div>

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 18 '21 at 05:12
0

While I'm not an expert on CSS modules, I did find this documentation: https://github.com/css-modules/css-modules/blob/master/docs/import-multiple-css-modules.md

It appears that you'll need to combine the styles for active and sideMenu together using Object.assign

Michael Camden
  • 1,183
  • 8
  • 9
  • I fiddled around with this - but I'm not sure I see what the target would be in this case - I need a class added based upon a boolean in props, and a class from the css module, and both should be prefixed with `styles.`. – Toby Jul 14 '16 at 20:09
0

import classNames from 'classnames/bind'.

then you can use like this:
let cx = classNames.bind(styles);

  • 1
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Oct 18 '21 at 13:56
0

In case like that styles['bar-item-active'] , you can wrap it . in second square brackets like [styles['bar-item-active']] : your condition

MAKEDA
  • 61
  • 7
0

I don't think anyone has suggested using both the style and the className attributes in your React DOM element:

const sideMenu={backgroundColour:'blue',
                border:'1px black solid'}

return <>
  <div style={sideMenu} className={this.props.menuOpen ? styles.inactive : styles.active}>
  ...
  </div>
</>

It's not the neatest solution, but it does avoid adding another dependency to your project and if your sideMenu class is small then it could be a option

We'll See
  • 122
  • 1
  • 8
0

Using classnames library

import classNames from "classnames";

classNames is a function. if you pass some strings, it joins them together if they all are truthy. If there is any falsy string it will not pass it. For example

 let textColor=undefined
 classNames(textColor, "px-2","py-2" )

since textColor variable is falsy, classNames will ignore it and returns this string

"px-2 py-2"

You can also pass an object to classNames function.

 const active=true
 const foo=false

Let's say I have this expression

classNames({
    'text-green':active,
     'text-yellow':foo
})

classNames function will look at "VALUE" of each pair. if the "value" is truthy, it will take the "KEY" but if the "VALUE" is falsy it will ignore the "KEY". In this example since "active" true and "foo" is false it will return this string

 'text-green'

In your example, you want to add a className based on if the prop is truth or falsy in other words if the prop exists or not:

let classNames=classNames(
    styles.sideMenu,
    // based on props that you are passing you can define dynamic classnames in your component
    {
      "active":this.props.menuOpen
    }
  )
Yilmaz
  • 35,338
  • 10
  • 157
  • 202