474

I want to conditionally show and hide this button group depending on what is passed in from the parent component which looks like this:

    <TopicNav showBulkActions={this.__hasMultipleSelected} />

    __hasMultipleSelected: function() {
      return false; //return true or false depending on data
    }
    var TopicNav = React.createClass({
    render: function() {
    return (
        <div className="row">
            <div className="col-lg-6">
                <div className="btn-group pull-right {this.props.showBulkActions ? 'show' : 'hidden'}">
                    <button type="button" className="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
                      Bulk Actions <span className="caret"></span>
                    </button>
                    <ul className="dropdown-menu" role="menu">
                      <li><a href="#">Merge into New Session</a></li>
                      <li><a href="#">Add to Existing Session</a></li>
                      <li className="divider"></li>
                      <li><a href="#">Delete</a></li>
                    </ul>
                </div>
            </div>
        </div>
        );
      }
    });

Nothing is happening however, with the {this.props.showBulkActions ? 'show' : 'hidden'}. Am I doing anything wrong here?

Samia Rahman
  • 41
  • 1
  • 8
apexdodge
  • 6,657
  • 4
  • 26
  • 33
  • You may also wish to consider [react-bootstrap](https://react-bootstrap.github.io/), since this abstracts some of the class stuff into component properties, making what you're trying to do a little easier. – Dancrumb Jun 14 '17 at 17:31

24 Answers24

827

The curly braces are inside the string, so it is being evaluated as string. They need to be outside, so this should work:

<div className={"btn-group pull-right " + (this.props.showBulkActions ? 'show' : 'hidden')}>

Note the space after "pull-right". You don't want to accidentally provide the class "pull-rightshow" instead of "pull-right show". Also the parentheses needs to be there.

Oskar Eriksson
  • 845
  • 1
  • 9
  • 28
spitfire109
  • 9,651
  • 3
  • 20
  • 28
  • 3
    Thanks! I had to modify it slightly because for some reason it wasn't outputting btn-group pull-right at all. Just show or hidden. – apexdodge May 29 '15 at 16:01
  • 1
    This is particularly helpful in certain cases where `classnames` might not be appropriate. If you are in your `render` function and you have a `map`, you might only know whether you want to add a class at the time you are rendering it, so this answer is quite useful for that. – Dave Cooper Nov 11 '16 at 17:12
  • great alternative instead of having a bunch of conditional templates in your render or return – devonj May 16 '17 at 20:57
  • @apexdodge what modification you had to do. I have the same issue. – RamY Jun 06 '18 at 06:56
  • 1
    @RamY One way is to put all the classes inside the conditional `this.props.showBulkActions ? 'btn-group pull-right show' : 'btn-group pull-right hidden')`. Not elegant but it works. – Ian Jan 31 '19 at 14:11
  • the way react handle this is so out of convention. – allenhwkim Feb 27 '22 at 04:00
128

As others have commented, classnames utility is the currently recommended approach to handle conditional CSS class names in ReactJs.

In your case, the solution will look like:

var btnGroupClasses = classNames(
  'btn-group',
  'pull-right',
  {
    'show': this.props.showBulkActions,
    'hidden': !this.props.showBulkActions
  }
);

...

<div className={btnGroupClasses}>...</div>

As a side note, I would suggest you to try to avoid using both show and hidden classes, so the code could be simpler. Most likely, you don't need to set a class for something to be shown by default.

2021 addendum: for performance improvement, you can look into clsx as an alternative.

Diego V
  • 6,189
  • 7
  • 40
  • 45
  • 22
    Could you elaborate on classNames utility being the "currently recommended approach"? Is that captured in some well-regarded best practices document somewhere? Or just sort of the word of mouth around React and `classNames` at the moment? – Alexander Nied Apr 21 '17 at 01:52
  • 10
    @anied At the time of writing it was recommended in official React documentation: http://web.archive.org/web/20160602124910/http://facebook.github.io:80/react/docs/class-name-manipulation.html – Diego V Apr 24 '17 at 23:44
  • 7
    It is still mentioned in [latest documentation](https://web.archive.org/web/20180829094008/https://reactjs.org/docs/faq-styling.html#how-do-i-add-css-classes-to-components): "*If you often find yourself writing code like this, classnames package can simplify it.*" – Franklin Yu Sep 04 '18 at 21:17
  • 2
    As of 2021, instead of `classnames` you might want to try [clsx](https://www.npmjs.com/package/clsx) – Kevin Law Nov 26 '21 at 10:38
  • 1
    _classnames_ is still mentioned [here](https://react.dev/reference/react-dom/components/common#how-to-apply-multiple-css-classes-conditionally) - "_To make this more readable, you can use a tiny helper library like classnames_" – jna Mar 29 '23 at 21:55
108

If you are using a transpiler (such as Babel or Traceur) you can use the new ES6 "template strings".

Here is the answer of @spitfire109, modified accordingly:

<div className={`btn-group pull-right ${this.props.showBulkActions ? 'shown' : 'hidden'}`}>

This approach allows you to do neat things like that, rendering either s-is-shown or s-is-hidden:

<div className={`s-${this.props.showBulkActions ? 'is-shown' : 'is-hidden'}`}>
Bertrand
  • 1,718
  • 2
  • 13
  • 24
  • 35
    Be careful with the second approach, especially in large codebases, as it makes the class strings less greppable. For example, if someone searches for `s-is-shown` or `s-is-hidden` in the codebase, they won't find this code. – mark Mar 21 '18 at 21:33
25

you can simply do the following for example.

let classNameDependsOnCondtion = i18n.language == 'en' ? "classname" : "";

className={`flex flex-col lg:flex-row list-none ${classNameDependsOnCondtion }`}

OR

className={`flex flex-col lg:flex-row list-none ${i18n.language == 'en' ? "classname" : ""}`}
Hasan Zahran
  • 1,364
  • 16
  • 14
  • 2
    If you are conditionally rendering a single className inline and don't want a class to appear for one of the conditions, you can return `null` or `undefined` instead of an empty string. Example: `className={condition ? 'red' : null}` or `className={condition ? 'red' : undefined}` It's better to keep your markup clean rather than having elements appear like `
    ` or `
    – Anthony Avila Feb 12 '22 at 02:08
  • 1
    @AnthonyAvila true, also its better to use classnames https://www.npmjs.com/package/classnames to avoid printing extra empty spaces. – Hasan Zahran Feb 14 '22 at 17:32
21

You can use here String literals

const Angle = ({show}) => {

   const angle = `fa ${show ? 'fa-angle-down' : 'fa-angle-right'}`;

   return <i className={angle} />
}
dhilt
  • 18,707
  • 8
  • 70
  • 85
Musa
  • 2,596
  • 26
  • 25
17

Replace:

<div className="btn-group pull-right {this.props.showBulkActions ? 'show' : 'hidden'}">`

with:

<div className={`btn-group pull-right ${this.props.showBulkActions ? 'show' : 'hidden'}`}
Amit Agarwal
  • 10,910
  • 1
  • 32
  • 43
15

In case you will need only one optional class name:

<div className={"btn-group pull-right " + (this.props.showBulkActions ? "show" : "")}>
Eugene Chybisov
  • 1,634
  • 2
  • 23
  • 32
14

Or use npm classnames. It is very easy and useful especially for constructing the list of classes

Maksim Kalmykov
  • 1,293
  • 3
  • 20
  • 26
Pencilcheck
  • 2,664
  • 3
  • 25
  • 14
13

Expending on @spitfire109's fine answer, one could do something like this:

rootClassNames() {
  let names = ['my-default-class'];
  if (this.props.disabled) names.push('text-muted', 'other-class');

  return names.join(' ');
}

and then within the render function:

<div className={this.rootClassNames()}></div>

keeps the jsx short

flaky
  • 6,816
  • 4
  • 29
  • 46
12

You can use ES6 arrays instead of classnames. The answer is based on Dr. Axel Rauschmayer article: Conditionally adding entries inside Array and object literals.

<div className={[
                 "classAlwaysPresent", 
                 ...Array.from(condition && ["classIfTrue"])
                ].join(" ")} />
Tudor Morar
  • 3,720
  • 2
  • 27
  • 25
12

2019:

React is lake a lot of utilities. But you don't need any npm package for that. just create somewhere the function classnames and call it when you need it;

function classnames(obj){
  return Object.entries(obj).filter( e => e[1] ).map( e=>e[0] ).join(' ');
}

or

function classnames(obj){
 return Object.entries(obj).map( ([cls,enb]) => enb? cls: '' ).join(' ');
}

example

  stateClass= {
    foo:true,
    bar:false,
    pony:2
  }
  classnames(stateClass) // return 'foo pony'


 <div className="foo bar {classnames(stateClass)}"> some content </div>

Just For Inspiration

declaring helper DOM element and using it native toggle method:

(DOMToken​List)classList.toggle(class,condition)

example:

const classes = document.createElement('span').classList; 

function classstate(obj){
  for( let n in obj) classes.toggle(n,obj[n]);
 return classes; 
}
 
pery mimon
  • 7,713
  • 6
  • 52
  • 57
10
<div className={['foo', condition && 'bar'].filter(Boolean).join(' ')} />

.filter(Boolean) removes "falsey" values from the array. Since class names must be strings, anything other than that would not be included in the new filtered array.

console.log(  ['foo', true  && 'bar'].filter(Boolean).join(' ')  )
console.log(  ['foo', false && 'bar'].filter(Boolean).join(' ')  )

Above written as a function:

const cx = (...list) => list.filter(Boolean).join(' ')

// usage:
<div className={cx('foo', condition && 'bar')} />

var cx = (...list) => list.filter(Boolean).join(' ')
console.log(  cx('foo', 1 && 'bar', 1 && 'baz')  )
console.log(  cx('foo', 0 && 'bar', 1 && 'baz')  )
console.log(  cx('foo', 0 && 'bar', 0 && 'baz')  )
vsync
  • 118,978
  • 58
  • 307
  • 400
8

More elegant solution, which is better for maintenance and readability:

const classNames = ['js-btn-connect'];

if (isSelected) { classNames.push('is-selected'); }

<Element className={classNames.join(' ')}/>
Nate Ben
  • 626
  • 9
  • 14
8

simply use this approach--

<div className={`${this.props.showActions ? 'shown' : 'hidden'}`}>

this is much more neat and clean.

Suraj
  • 802
  • 9
  • 7
4

you can use this:

<div className={"btn-group pull-right" + (this.props.showBulkActions ? ' show' : ' hidden')}>
AmirBll
  • 1,081
  • 1
  • 13
  • 25
3

This is useful when you have more than one class to append. You can join all classes in array with a space.

const visibility = this.props.showBulkActions ? "show" : ""
<div className={["btn-group pull-right", visibility].join(' ')}>
kooskoos
  • 4,622
  • 1
  • 12
  • 29
3

I have tried to tailored my answer to include all the best possible solution in the post

There are many different ways of getting this done.

1. Inline inside the class

<div className={`... ${this.props.showBulkActions ? 'show' : 'hidden'}`}>
  ...
</div>

2. Using the values

var btnClass = classNames(
  ...
  {
    'show': this.props.showBulkActions,
    'hidden': !this.props.showBulkActions
  }
);

3. Using a variable

let dependentClass = this.props.showBulkActions ? 'show' : 'hidden';

className={`... ${dependentClass }`}

4. Using clsx

<div className={clsx('...',`${this.props.showBulkActions ? 'show' : 'hidden'}`)}>
  ...
</div>
krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88
1

This would work for you

var TopicNav = React.createClass({
render: function() {

let _myClasses = `btn-group pull-right {this.props.showBulkActions?'show':'hidden'}`;

return (
            ...
            <div className={_myClasses}>
               ...
            </div>
    );
  }
});
brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
Manoj Amalraj
  • 535
  • 1
  • 5
  • 14
1

Reference to @split fire answer, we can update it with template literals, which is more readable,For reference Checkout javascript template literal

<div className={`btn-group pull-right ${this.props.showBulkActions ? 'show' : 'hidden'}`}>
Akhil Aravind
  • 5,741
  • 16
  • 35
0

You can use this npm package. It handles everything and has options for static and dynamic classes based on a variable or a function.

// Support for string arguments
getClassNames('class1', 'class2');

// support for Object
getClassNames({class1: true, class2 : false});

// support for all type of data
getClassNames('class1', 'class2', null, undefined, 3, ['class3', 'class4'], { 
    class5 : function() { return false; },
    class6 : function() { return true; }
});

<div className={getClassNames('show', {class1: true, class2 : false})} /> // "show class1"
Tushar Sharma
  • 192
  • 1
  • 15
0

Based on the value of this.props.showBulkActions you can switch classes dynamically as follows.

<div ...{...this.props.showBulkActions 
? { className: 'btn-group pull-right show' } 
: { className: 'btn-group pull-right hidden' }}>
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
Vladimir Trifonov
  • 1,395
  • 11
  • 9
0

I would like to add that you can also use a variable content as a part of the class

<img src={src} alt="Avatar" className={"img-" + messages[key].sender} />

The context is a chat between a bot and a user, and the styles change depending of the sender, this is the browser result:

<img src="http://imageurl" alt="Avatar" class="img-bot">
J C
  • 731
  • 7
  • 11
0

A function to return the correct class based on a param (if present)

  getClass(param){
    let podClass = 'classA'
    switch(param.toLowerCase()){
        case 'B':
            podClass  = 'classB'
            break;
        case 'C':
            podClass  = 'classC'
            break;
    }
    return podClass
}

Now just invoke this function from the div where the corresponding class is to be applied.

 <div className={anyOtherClass + this.getClass(param)}

I successfully used this logic to apply the correct color to my bootstrap table rows.

prakhar3agrwal
  • 316
  • 3
  • 12
0
<div className={"h-3 w-3 rounded-full my-auto " + (index.endDate ==="present"? "bg-green-500":"bg-red-500")}></div>

Don't Forget to add an extra space after the static class names.

CsAlkemy
  • 152
  • 1
  • 6