UNPKG

9.07 kBMarkdownView Raw
1# Classnames
2
3> A simple JavaScript utility for conditionally joining classNames together.
4
5<p>
6 <a aria-label="NPM version" href="https://www.npmjs.com/package/classnames">
7 <img alt="" src="https://img.shields.io/npm/v/classnames.svg?style=for-the-badge&labelColor=0869B8">
8 </a>
9 <a aria-label="License" href="#">
10 <img alt="" src="https://img.shields.io/npm/l/classnames.svg?style=for-the-badge&labelColor=579805">
11 </a>
12 <a aria-label="Thinkmill Logo" href="https://www.thinkmill.com.au/open-source?utm_campaign=github-classnames">
13 <img src="https://img.shields.io/badge/Sponsored%20BY%20Thinkmill-ed0000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTg2IiBoZWlnaHQ9IjU4NiIgdmlld0JveD0iMCAwIDU4NiA1ODYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xOTk2XzQwNikiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTU4NiAyOTNDNTg2IDQ1NC44MTkgNDU0LjgxOSA1ODYgMjkzIDU4NkMxMzEuMTgxIDU4NiAwIDQ1NC44MTkgMCAyOTNDMCAxMzEuMTgxIDEzMS4xODEgMCAyOTMgMEM0NTQuODE5IDAgNTg2IDEzMS4xODEgNTg2IDI5M1pNMjA1Ljc3NiAzNTguOTQ0QzE5MS4zNzYgMzU4Ljk0NCAxODUuOTA0IDM1Mi4zMiAxODUuOTA0IDMzNS45MDRWMjYyLjc1MkgyMTQuNDE2VjIzNy42OTZIMTg1LjkwNFYyMDEuMTJIMTUzLjA3MlYyMzcuNjk2SDEyOC41OTJWMjYyLjc1MkgxNTMuMDcyVjM0MC44QzE1My4wNzIgMzcyLjc2OCAxNjYuNjA4IDM4NS43MjggMTk3LjQyNCAzODUuNzI4QzIwMy40NzIgMzg1LjcyOCAyMTAuOTYgMzg0LjU3NiAyMTUuODU2IDM4My4xMzZWMzU3LjUwNEMyMTMuNTUyIDM1OC4zNjggMjA5LjUyIDM1OC45NDQgMjA1Ljc3NiAzNTguOTQ0Wk00MDcuMzc2IDIzNC4yNEMzODUuMiAyMzQuMjQgMzcxLjA4OCAyNDQuMDMyIDM2MC40MzIgMjYwLjczNkMzNTIuOTQ0IDI0My40NTYgMzM3LjM5MiAyMzQuMjQgMzE3LjIzMiAyMzQuMjRDMjk5Ljk1MiAyMzQuMjQgMjg2Ljk5MiAyNDEuMTUyIDI3Ni42MjQgMjU1LjI2NEgyNzYuMDQ4VjIzNy42OTZIMjQ0LjY1NlYzODRIMjc3LjQ4OFYzMDUuNjY0QzI3Ny40ODggMjc3LjQ0IDI4OC43MiAyNjAuNzM2IDMwOC4zMDQgMjYwLjczNkMzMjUuMjk2IDI2MC43MzYgMzM0LjUxMiAyNzIuODMyIDMzNC41MTIgMjkzLjU2OFYzODRIMzY3LjM0NFYzMDUuMDg4QzM2Ny4zNDQgMjc3LjE1MiAzNzguODY0IDI2MC43MzYgMzk4LjE2IDI2MC43MzZDNDE0LjU3NiAyNjAuNzM2IDQyNC42NTYgMjcxLjEwNCA0MjQuNjU2IDI5Ny4wMjRWMzg0SDQ1Ny40ODhWMjkzLjg1NkM0NTcuNDg4IDI1NC40IDQzOC40OCAyMzQuMjQgNDA3LjM3NiAyMzQuMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzE5OTZfNDA2Ij4KPHJlY3Qgd2lkdGg9IjU4NiIgaGVpZ2h0PSI1ODYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==&labelColor=C60200&locoColor=white&logoWidth=0">
14 </a>
15
16 </p>
17
18Install from the [npm registry](https://www.npmjs.com/) with your package manager:
19```bash
20npm install classnames
21```
22
23Use with [Node.js](https://nodejs.org/en/), [Browserify](https://browserify.org/), or [webpack](https://webpack.github.io/):
24
25```js
26const classNames = require('classnames');
27classNames('foo', 'bar'); // => 'foo bar'
28```
29
30Alternatively, 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.
31
32### Project philosophy
33
34We 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 implications before being released, and we have a comprehensive test suite.
35
36Classnames follows the [SemVer](https://semver.org/) standard for versioning.
37
38There is also a [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md).
39
40## Usage
41
42The `classNames` function takes any number of arguments which can be a string or object.
43The 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.
44
45```js
46classNames('foo', 'bar'); // => 'foo bar'
47classNames('foo', { bar: true }); // => 'foo bar'
48classNames({ 'foo-bar': true }); // => 'foo-bar'
49classNames({ 'foo-bar': false }); // => ''
50classNames({ foo: true }, { bar: true }); // => 'foo bar'
51classNames({ foo: true, bar: true }); // => 'foo bar'
52
53// lots of arguments of various types
54classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
55
56// other falsy values are just ignored
57classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
58```
59
60Arrays will be recursively flattened as per the rules above:
61
62```js
63const arr = ['b', { c: true, d: false }];
64classNames('a', arr); // => 'a b c'
65```
66
67### Dynamic class names with ES2015
68
69If you're in an environment that supports [computed keys](https://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) (available in ES2015 and Babel) you can use dynamic class names:
70
71```js
72let buttonType = 'primary';
73classNames({ [`btn-${buttonType}`]: true });
74```
75
76### Usage with React.js
77
78This package is the official replacement for `classSet`, which was originally shipped in the React.js Addons bundle.
79
80One 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:
81
82```js
83import React, { useState } from 'react';
84
85export default function Button (props) {
86 const [isPressed, setIsPressed] = useState(false);
87 const [isHovered, setIsHovered] = useState(false);
88
89 let btnClass = 'btn';
90 if (isPressed) btnClass += ' btn-pressed';
91 else if (isHovered) btnClass += ' btn-over';
92
93 return (
94 <button
95 className={btnClass}
96 onMouseDown={() => setIsPressed(true)}
97 onMouseUp={() => setIsPressed(false)}
98 onMouseEnter={() => setIsHovered(true)}
99 onMouseLeave={() => setIsHovered(false)}
100 >
101 {props.label}
102 </button>
103 );
104}
105```
106
107You can express the conditional classes more simply as an object:
108
109```js
110import React, { useState } from 'react';
111import classNames from 'classnames';
112
113export default function Button (props) {
114 const [isPressed, setIsPressed] = useState(false);
115 const [isHovered, setIsHovered] = useState(false);
116
117 const btnClass = classNames({
118 btn: true,
119 'btn-pressed': isPressed,
120 'btn-over': !isPressed && isHovered,
121 });
122
123 return (
124 <button
125 className={btnClass}
126 onMouseDown={() => setIsPressed(true)}
127 onMouseUp={() => setIsPressed(false)}
128 onMouseEnter={() => setIsHovered(true)}
129 onMouseLeave={() => setIsHovered(false)}
130 >
131 {props.label}
132 </button>
133 );
134}
135```
136
137Because 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:
138
139```js
140const btnClass = classNames('btn', this.props.className, {
141 'btn-pressed': isPressed,
142 'btn-over': !isPressed && isHovered,
143});
144```
145
146### Alternate `dedupe` version
147
148There 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.
149
150This version is slower (about 5x) so it is offered as an opt-in.
151
152To use the dedupe version with Node.js, Browserify, or webpack:
153
154```js
155const classNames = require('classnames/dedupe');
156
157classNames('foo', 'foo', 'bar'); // => 'foo bar'
158classNames('foo', { foo: false, bar: true }); // => 'bar'
159```
160
161For standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page.
162
163### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules))
164
165If 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.
166
167_Note that in ES2015 environments, it may be better to use the "dynamic class names" approach documented above._
168
169```js
170const classNames = require('classnames/bind');
171
172const styles = {
173 foo: 'abc',
174 bar: 'def',
175 baz: 'xyz',
176};
177
178const cx = classNames.bind(styles);
179
180const className = cx('foo', ['bar'], { baz: true }); // => 'abc def xyz'
181```
182
183Real-world example:
184
185```js
186/* components/submit-button.js */
187import { useState } from 'react';
188import classNames from 'classnames/bind';
189import styles from './submit-button.css';
190
191const cx = classNames.bind(styles);
192
193export default function SubmitButton ({ store, form }) {
194 const [submissionInProgress, setSubmissionInProgress] = useState(store.submissionInProgress);
195 const [errorOccurred, setErrorOccurred] = useState(store.errorOccurred);
196 const [valid, setValid] = useState(form.valid);
197
198 const text = submissionInProgress ? 'Processing...' : 'Submit';
199 const className = cx({
200 base: true,
201 inProgress: submissionInProgress,
202 error: errorOccurred,
203 disabled: valid,
204 });
205
206 return <button className={className}>{text}</button>;
207}
208```
209
210## Polyfills needed to support older browsers
211
212#### `classNames >=2.0.0`
213
214`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.
215
216## LICENSE [MIT](LICENSE)
217
218Copyright (c) 2018 Jed Watson.
219Copyright of the Typescript bindings are respective of each contributor listed in the definition file.
220
\No newline at end of file