react-css-modules
DefinitelyTyped icon, indicating that this package has TypeScript declarations provided by the separate @types/react-css-modules package

1.2.0 • Public • Published

React CSS Modules

Travis build status NPM version

React CSS Modules implement automatic mapping of class names to CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS!

What's the Problem?

CSS modules are awesome. If you are not familiar with CSS modules, it is a concept of using a module bundler such as webpack to load CSS scoped to a particular document. CSS modules loader will generate a unique name for a each CSS class at the time of loading the CSS. Refer to webpack-demo for a full example.

In the context of React, this looks like this:

import React from 'react';
import styles from './car.css';

export default class Car extends React.Component {
    render () {
        return <div className={styles.car}>
            <div className={styles.frontDoor}></div>
            <div className={styles.backDoor}></div>
        </div>;
    }
}

Rendering the component will produce a markup similar to:

<div class="car__car___32osj" data-reactid=".0.0">
    <div class="car__front-door___2w27N" data-reactid=".0.0.$=10:0">front-door</div>
    <div class="car__back-door___1oVw5" data-reactid=".0.0.$=11:0">back-door</div>
</div>

and a corresponding CSS file that matches those CSS classes.

Awesome!

However, this approach has several disadvantages:

  • You have to use camelCase CSS class names.
  • You have to use styles object whenever constructing a className.

React CSS Modules enables seamless CSS modules for React, e.g.

import React from 'react';
import styles from './car.css';
import CSSModules from 'react-css-modules';

class Car extends React.Component {
    render () {
        return <div className='car'>
            <div className='front-door'></div>
            <div className='back-door'></div>
        </div>;
    }
}

export default CSSModules(Car, styles);

CSSModules extends Car render method. It will look for CSS classes in ./car.css that match CSS class names in ReactElement className and will replace/append the matching unique class names to className declaration.

Refer to the react-css-modules-examples repository for a complete usage example.

Awesome!

Usage

Setup consists of:

  • Setting up a module bundler to load your ICSS.
  • Decorating your component using react-css-modules.

Module Bundler

webpack

{
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
}
  • Setup extract-text-webpack-plugin plugin:
new ExtractTextPlugin('app.css', {
    allChunks: true
})

Refer to webpack-demo or react-css-modules-examples for a complete setup.

Browserify

Refer to css-modulesify.

Decorator

/**
 * @typedef CSSModules~Options
 * @property {Boolean} allowMultiple Determines whether `className` can have multiple class names. Throws an error when the constrained is not met. Default: true.
 * @property {Boolean} keepOriginal Determines whether the original `className` value is kept in addition to the appended CSS modules styles CSS class name. Default: true.
 * @property {Boolean} errorNotFound Determines whether an error is raised if `className` defines a CSS class(es) that is not present in the CSS modules styles. Default: false.
 */

/**
 * @param {ReactClass} Component
 * @param {Object} styles CSS modules class map.
 * @param {CSSModules~Options} options
 * @return {ReactClass}
 */

You need to decorate your component using react-css-modules, e.g.

import React from 'react';
import styles from './car.css';
import CSSModules from 'react-css-modules';

class Car extends React.Component {
    render () {
        return <div className='car'>
            <div className='front-door'></div>
            <div className='back-door'></div>
        </div>;
    }
}

export default CSSModules(Car, styles);

Thats it!

As the name implies, react-css-modules is compatible with the ES7 decorators syntax:

import React from 'react';
import styles from './car.css';
import CSSModules from 'react-css-modules';

@CSSModules(styles)
export default class extends React.Component {
    render () {
        return <div className='car'>
            <div className='front-door'>front-door</div>
            <div className='back-door'>back-door</div>
        </div>;
    }
}

Awesome!

Options

Options are supplied as the third parameter to the CSSModules function.

CSSModules(Component, styles, options);

or as a second parameter when using CSSModules as a decorator:

@CSSModules(styles, options);

useModuleName

Default: false.

When enabled then CSS Modules are loaded using moduleName property and className is used only for global CSS, e.g.

<div className='global-css-class' moduleName='local-module-name' />

allowMultiple

Default: true.

Allows multiple CSS class names.

When false, the following will cause an error:

<div className='foo bar' />

keepOriginal

Default: true.

Keeps original CSS class name in addition to the names of the CSS Modules.

When true, the following ReactElement:

<div className='foo bar' />

will be rendered with a className property foo component__foo___2w27N bar component__bar__1oVw5.

errorNotFound

Default: false.

Throws an error when class name cannot be mapped to a CSS Module.

SASS, SCSS, LESS and other CSS Preprocessors

ICSS is compatible with the CSS Preprocessors. All you need is to add the preprocessor to the chain of loaders, e.g. in the case of webpack it is as simple as installing sass-loader and adding !sass to the end of the style-loader loader chain declaration (loaders are processed from right to left):

{
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass')
}

Global CSS

CSS Modules does not restrict you from using global CSS.

:global .foo {

}

When using global CSS, you need to enable keepOriginal option.

Use global CSS with caution. With CSS Modules, there are only a handful of valid use cases for global CSS (e.g. normalization).

Multiple CSS Classes

CSS modules promote composition pattern, i.e. every CSS class that is used in a component should define all properties required to describe the element, e.g.

.button {

}

.active {
    composes: common;

    /* anything that only applies to active state of the button */
}

.disabled {
    composes: common;

    /* anything that only applies to disabled state of the button */
}

To learn more about composing CSS rules, I suggest reading Glen Maddern article about CSS Modules and the official CSS modules spec.

Using React CSS Modules, you can map as many CSS classes to the element as you want. CSSModules will append a unique class name for every class name it matches in the className declaration, e.g.

.button {

}

.active {

}
<div className='button active'></div>

This will map both ICSS CSS classes to the target element.

However, I encourage you to use composition whenever possible. Composition promotes better separation of markup from style sheets using semantics that would be hard to achieve without CSS modules. You can enforce one CSS class name per className using allowMultiple option.

Package Sidebar

Install

npm i react-css-modules@1.2.0

Version

1.2.0

License

BSD-3-Clause

Last publish

Collaborators

  • gajus