Multiple classNames with CSS Modules and React
Asked Answered
T

11

52

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.

Trickster answered 14/7, 2016 at 18:56 Comment(2)
Why wouldn't classnames work for you? This is the case it's designed for.Majors
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?Trickster
M
83

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 : '');
Majors answered 14/7, 2016 at 20:19 Comment(2)
{ [styles.active]: this.props.menuOpen } what is the syntax? i can't find it in es6Boutin
@Boutin They're called computed property names. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Majors
S
24

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>
Syndicate answered 26/7, 2019 at 9:16 Comment(3)
Works fine for me. FYI: you missed a } at the end.Thigh
I appreciate this solution because it doesn't need an import.Babbette
Great thanks. This is much handier.Highpriced
S
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.

Suellen answered 2/9, 2019 at 5:16 Comment(0)
M
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>
Maddox answered 13/8, 2017 at 18:49 Comment(0)
T
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.

Trickster answered 14/7, 2016 at 20:20 Comment(0)
O
2

Using Array.join

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

Obstacle answered 17/10, 2021 at 22:57 Comment(1)
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.Compartmentalize
H
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

Hunt answered 14/7, 2016 at 19:43 Comment(1)
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..Trickster
C
0

import classNames from 'classnames/bind'.

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

Chartism answered 18/10, 2021 at 9:9 Comment(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: stackoverflow.com/help/how-to-answer . Good luck 🙂Kelle
L
0

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

Lucite answered 30/7, 2022 at 13:38 Comment(0)
C
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

Cyanic answered 30/8, 2022 at 18:27 Comment(0)
D
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
    }
  )
Dom answered 16/12, 2022 at 23:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.