1Classnames 2=========== 3 4[![Version](http://img.shields.io/npm/v/classnames.svg)](https://www.npmjs.org/package/classnames) 5[![Build Status](https://travis-ci.org/JedWatson/classnames.svg?branch=master)](https://travis-ci.org/JedWatson/classnames) 6[![Supported by Thinkmill](https://thinkmill.github.io/badge/heart.svg)](http://thinkmill.com.au/?utm_source=github&utm_medium=badge&utm_campaign=classnames) 7 8A simple JavaScript utility for conditionally joining classNames together. 9 10Install with [npm](https://www.npmjs.com/), [Bower](https://bower.io/), or [Yarn](https://yarnpkg.com/): 11 12npm: 13```sh 14npm install classnames --save 15``` 16 17Bower: 18```sh 19bower install classnames --save 20``` 21 22Yarn (note that `yarn add` automatically saves the package to the `dependencies` in `package.json`): 23```sh 24yarn add classnames 25``` 26 27Use with [Node.js](https://nodejs.org/en/), [Browserify](http://browserify.org/), or [webpack](https://webpack.github.io/): 28 29```js 30var classNames = require('classnames'); 31classNames('foo', 'bar'); // => 'foo bar' 32``` 33 34Alternatively, you can simply include `index.js` on your page with a standalone `<script>` tag and it will export a global `classNames` method, or define the module if you are using RequireJS. 35 36### Project philosophy 37 38We take the stability and performance of this package seriously, because it is run millions of times a day in browsers all around the world. Updates are thoroughly reviewed for performance impacts before being released, and we have a comprehensive test suite. 39 40Classnames follows the [SemVer](http://semver.org/) standard for versioning. 41 42There is also a [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md). 43 44## Usage 45 46The `classNames` function takes any number of arguments which can be a string or object. 47The argument `'foo'` is short for `{ foo: true }`. If the value associated with a given key is falsy, that key won't be included in the output. 48 49```js 50classNames('foo', 'bar'); // => 'foo bar' 51classNames('foo', { bar: true }); // => 'foo bar' 52classNames({ 'foo-bar': true }); // => 'foo-bar' 53classNames({ 'foo-bar': false }); // => '' 54classNames({ foo: true }, { bar: true }); // => 'foo bar' 55classNames({ foo: true, bar: true }); // => 'foo bar' 56 57// lots of arguments of various types 58classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux' 59 60// other falsy values are just ignored 61classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1' 62``` 63 64Arrays will be recursively flattened as per the rules above: 65 66```js 67var arr = ['b', { c: true, d: false }]; 68classNames('a', arr); // => 'a b c' 69``` 70 71### Dynamic class names with ES2015 72 73If you're in an environment that supports [computed keys](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) (available in ES2015 and Babel) you can use dynamic class names: 74 75```js 76let buttonType = 'primary'; 77classNames({ [`btn-${buttonType}`]: true }); 78``` 79 80### Usage with React.js 81 82This package is the official replacement for `classSet`, which was originally shipped in the React.js Addons bundle. 83 84One of its primary use cases is to make dynamic and conditional `className` props simpler to work with (especially more so than conditional string manipulation). So where you may have the following code to generate a `className` prop for a `<button>` in React: 85 86```js 87var Button = React.createClass({ 88 // ... 89 render () { 90 var btnClass = 'btn'; 91 if (this.state.isPressed) btnClass += ' btn-pressed'; 92 else if (this.state.isHovered) btnClass += ' btn-over'; 93 return <button className={btnClass}>{this.props.label}</button>; 94 } 95}); 96``` 97 98You can express the conditional classes more simply as an object: 99 100```js 101var classNames = require('classnames'); 102 103var Button = React.createClass({ 104 // ... 105 render () { 106 var btnClass = classNames({ 107 btn: true, 108 'btn-pressed': this.state.isPressed, 109 'btn-over': !this.state.isPressed && this.state.isHovered 110 }); 111 return <button className={btnClass}>{this.props.label}</button>; 112 } 113}); 114``` 115 116Because you can mix together object, array and string arguments, supporting optional `className` props is also simpler as only truthy arguments get included in the result: 117 118```js 119var btnClass = classNames('btn', this.props.className, { 120 'btn-pressed': this.state.isPressed, 121 'btn-over': !this.state.isPressed && this.state.isHovered 122}); 123``` 124 125 126### Alternate `dedupe` version 127 128There is an alternate version of `classNames` available which correctly dedupes classes and ensures that falsy classes specified in later arguments are excluded from the result set. 129 130This version is slower (about 5x) so it is offered as an opt-in. 131 132To use the dedupe version with Node.js, Browserify, or webpack: 133 134```js 135var classNames = require('classnames/dedupe'); 136 137classNames('foo', 'foo', 'bar'); // => 'foo bar' 138classNames('foo', { foo: false, bar: true }); // => 'bar' 139``` 140 141For standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page. 142 143 144### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules)) 145 146If you are using [css-modules](https://github.com/css-modules/css-modules), or a similar approach to abstract class "names" and the real `className` values that are actually output to the DOM, you may want to use the `bind` variant. 147 148_Note that in ES2015 environments, it may be better to use the "dynamic class names" approach documented above._ 149 150```js 151var classNames = require('classnames/bind'); 152 153var styles = { 154 foo: 'abc', 155 bar: 'def', 156 baz: 'xyz' 157}; 158 159var cx = classNames.bind(styles); 160 161var className = cx('foo', ['bar'], { baz: true }); // => "abc def xyz" 162``` 163 164Real-world example: 165 166```js 167/* components/submit-button.js */ 168import { Component } from 'react'; 169import classNames from 'classnames/bind'; 170import styles from './submit-button.css'; 171 172let cx = classNames.bind(styles); 173 174export default class SubmitButton extends Component { 175 render () { 176 let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit'; 177 let className = cx({ 178 base: true, 179 inProgress: this.props.store.submissionInProgress, 180 error: this.props.store.errorOccurred, 181 disabled: this.props.form.valid, 182 }); 183 return <button className={className}>{text}</button>; 184 } 185}; 186 187``` 188 189 190## Polyfills needed to support older browsers 191 192#### `classNames >=2.0.0` 193 194`Array.isArray`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill. 195 196`Object.keys`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill. This is only used in `dedupe.js`. 197 198## License 199 200[MIT](LICENSE). Copyright (c) 2017 Jed Watson. 201