Mankind1023
Mankind1023

Reputation: 7742

How to dynamically add a class to manual class names?

I need to add a dynamic class to a list of regular classes, but have no idea how (I'm using babel), something like this:

<div className="wrapper searchDiv {this.state.something}">
...
</div>

Any ideas?

Upvotes: 283

Views: 429979

Answers (18)

Marc M.
Marc M.

Reputation: 3791

The issue at hand I believe is whether a trailing space in the class name is HTML 5 complaint. The answer is yes.

When specified on HTML elements, the class attribute must have a value that is a set of space-separated tokens representing the various classes

https://html.spec.whatwg.org/multipage/dom.html#classes

A string containing a set of space-separated tokens may have leading or trailing ASCII whitespace.

https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#set-of-space-separated-tokens

With that settled, the obvious solution is to use a template literal as already mentioned.

<div className={`classOne classTwo ${stringOfMoreClasses || ''}`}>

If stringOfMoreClasses is nill, the className attribute will just include the original two classes with a single trailing white space.

Remember React re-runs the component function a lot! This is not a place where you want anything process heavy.

Upvotes: 0

Garret Wilson
Garret Wilson

Reputation: 21486

Someone mentioned the classnames library, which looks interesting and useful, but you could do the same thing yourself:

Original Version

…
  // keep your class names in an object,
  // enabled or disabled by the Boolean value
  const classNames = {
    MyComponent: true
    foo: false
    bar: someConditionVar
  };

  // construct the class name, e.g. "MyComponent bar"
  const className = Object.keys(classNames).filter(className =>
      classNames[className]).join(' ');

  // example usage in React JSX
  return (
    <div className="{className} …

Improved, Concise Version

Iterating through the object name/value entries rather than only the keys allows this to be shortened to a single statement:

…
  const className = Object.entries({
    MyComponent: true
    foo: false
    bar: someConditionVar
  }).filter(entry => entry[1]).map(entry => entry[0]).join(' ');

  // example usage in React JSX
  return (
    <div className="{className} …

Upvotes: 0

Delorean
Delorean

Reputation: 364

Simply combine class names within an array, remove empty values and join into the string.

<div className={["wrapper", "searchDiv",, undefined, null, this.state.something].filter((e) => e).join(" ")}"}>
...
</div>

Result is the className without extra spaces.

Have a creative day!

Upvotes: 0

Bennington
Bennington

Reputation: 318

A word of caution to those that are using back ticks or concatenation for className in React: You need to have spaces between your class names. Without them, you have a long word that doesn't match any of your css.

Upvotes: 0

Ihtisham Tanveer
Ihtisham Tanveer

Reputation: 388

[UPDATED Apr-21 2022: Adding curly brackets before and after backticks to prevent error]

you can simply use the condition for applying the specific class you want

<div className={`wrapper searchDiv ${this.state.something ? "class that you want" : ""}`}>

if you want to use the class of makeStyle you can use it like

<div className={`wrapper searchDiv ${this.state.something ? classes.specifiedClass : ""}`}>

Upvotes: 12

Daniel Paul
Daniel Paul

Reputation: 514

Don't think of a solution so complicated.

here is the easiest solution for your problem.

<div className={`wrapper searchDiv ${this.state.something}`}>
   ...
</div>

Upvotes: 14

Alvaro
Alvaro

Reputation: 11

Even though all of the answers above are quite good, the one that caught my attention is the string interpolation solution. So I'd like to propose a different approach to that using a custom StringBuilder.

Here's the implementation.

class StringBuilder {
  static Builder() {
    class Builder {
      constructor() {
        this.bucket = [];
      }

      append(str) {
        if (str !== null) {
          this.bucket.push(str);
        }
        return this;
      }

      build() {
        return this.bucket.join(' ');
      }
    }
    return new Builder();
  }
}

Then you would just use it like this within a component.

const Field = (props) => {
  const { label } = props;

  const [hasFocus, setFocus] = useState(false);

  const labelClasses = new StringBuilder.Builder()
    .append('form-control')
    .append(hasFocus ? 'has-focus' : null)
    .build();

  const turnOnFocus = () => setFocus(true);
  const turnOffFocus = () => setFocus(false);

  return (
    <div onClick={turnOnFocus} onBlur={turnOffFocus} className="form-group">
      <input
        type="text"
        defaultValue=""
        onChange={setValue}
      />
      <label className={labelClasses}>{label}</label>
    </div>
  );
};

In general, if you have more elements that require dynamic CSS classes, you could just instantiate another builder for that particular element. Additionally, if the class appears in more than one element with the same logic, then the solution would be extracting that logic to a method or function.

Upvotes: 0

quarterpi
quarterpi

Reputation: 863

If you're using css modules this is what worked for me.

const [condition, setCondition] = useState(false);

\\ toggle condition

return (
  <span className={`${styles.always} ${(condition ? styles.sometimes : '')`}>
  </span>
)

Upvotes: 3

user11889340
user11889340

Reputation:

const ClassToggleFC= () =>{
  
  const [isClass, setClass] = useState(false);

  const toggle =() => {
       setClass( prevState => !prevState)
  }
  
  return(
      <>
        <h1 className={ isClass ? "heading" : ""}> Hiii There </h1>
       <button onClick={toggle}>Toggle</button>
      </>
   )

}

I simply created a Function Component. Inside I take a state and set initial value is false..

I have a button for toggling state..

Whenever we change state rerender component and if state value (isClass) is false h1's className should be "" and if state value (isClass) is true h1's className is "heading"

Upvotes: 0

Hamza Khattabi
Hamza Khattabi

Reputation: 674

try this using hooks:

const [dynamicClasses, setDynamicClasses] = React.useState([
    "dynamicClass1", "dynamicClass2"
]);

and add this in className attribute :

<div className=`wrapper searchDiv ${[...dynamicClasses]}`>
...
</div>

to add class :

    const addClass = newClass => {
       setDynamicClasses([...dynamicClasses, newClass])
    }

to delete class :

        const deleteClass= classToDelete => {

           setDynamicClasses(dynamicClasses.filter(class = > {
             class !== classToDelete
           }));

        }

Upvotes: 8

Harjot Singh
Harjot Singh

Reputation: 29

className={css(styles.mainDiv, 'subContainer')}

This solution is tried and tested in React SPFx.

Also add import statement :

import { css } from 'office-ui-fabric-react/lib/Utilities';

Upvotes: 1

dannyjolie
dannyjolie

Reputation: 11369

You can either do this, normal JavaScript:

className={'wrapper searchDiv ' + this.state.something}

or the string template version, with backticks:

className={`wrapper searchDiv ${this.state.something}`}

Both types are of course just JavaScript, but the first pattern is the traditional kind.

Anyway, in JSX, anything enclosed in curly brackets is executed as JavaScript, so you can basically do whatever you want there. But combining JSX strings and curly brackets is a no-go for attributes.

Upvotes: 421

getBadgeClasses() {
    let classes = "badge m-2 ";
    classes += (this.state.count === 0) ? "badge-warning" : "badge-primary";
    return classes;
}

<span className={this.getBadgeClasses()}>Total Count</span>

Upvotes: 2

Sergey
Sergey

Reputation: 1075

If you need style names which should appear according to the state condition, I prefer to use this construction:

<div className={'wrapper searchDiv' + (this.state.something === "a" ? " anotherClass" : "")'}>

Upvotes: 8

Saad Mirza
Saad Mirza

Reputation: 1177

Here is the Best Option for Dynamic className , just do some concatenation like we do in Javascript.

     className={
        "badge " +
        (this.state.value ? "badge-primary " : "badge-danger ") +
        " m-4"
      }

Upvotes: 52

oligopol
oligopol

Reputation: 900

A simple possible syntax will be:

<div className={`wrapper searchDiv ${this.state.something}`}>

Upvotes: 53

Tushar Sharma
Tushar Sharma

Reputation: 302

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', ['class3', 'class4'], { 
    class5 : function() { return false; },
    class6 : function() { return true; }
});

<div className={getClassNames({class1: true, class2 : false})} />

Upvotes: 3

Brad Colthurst
Brad Colthurst

Reputation: 2605

Depending on how many dynamic classes you need to add as your project grows it's probably worth checking out the classnames utility by JedWatson on GitHub. It allows you to represent your conditional classes as an object and returns those that evaluate to true.

So as an example from its React documentation:

render () {

var btnClass = classNames({
  'btn': true,
  'btn-pressed': this.state.isPressed,
  'btn-over': !this.state.isPressed && this.state.isHovered
});

return <button className={btnClass}>I'm a button!</button>;

} 

Since React triggers a re-render when there is a state change, your dynamic class names are handled naturally and kept up to date with the state of your component.

Upvotes: 81

Related Questions