UNPKG

6.89 kBMarkdownView Raw
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
\No newline at end of file