react-css-modules

by gajus

Seamless mapping of class names to CSS modules inside of React components.

5.2K Stars 232 Forks Last release: over 1 year ago (v4.7.11) Other 309 Commits 49 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

React CSS Modules

GitSpo Mentions Travis build status NPM version js-canonical-style

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

⚠️⚠️⚠️ DEPRECATION NOTICE ⚠️⚠️⚠️

If you are considering to use

react-css-modules
, evaluate if
babel-plugin-react-css-modules
covers your use case.
babel-plugin-react-css-modules
is a lightweight alternative of
react-css-modules
.

babel-plugin-react-css-modules
is not a drop-in replacement and does not cover all the use cases of
react-css-modules
. However, it has a lot smaller performance overhead (0-10% vs +50%; see Performance) and a lot smaller size footprint (less than 2kb vs +17kb).

It is easy to get started! See the demo https://github.com/gajus/babel-plugin-react-css-modules/tree/master/demo

CSS Modules

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 module loader will generate a unique name for each CSS class at the time of loading the CSS document (Interoperable CSS to be precise). To see CSS Modules in practice, webpack-demo.

In the context of React, CSS Modules look like this:

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

export default class Table extends React.Component { render () { return

A0
B0
; } }

Rendering the component will produce a markup similar to:

A0
B0

and a corresponding CSS file that matches those CSS classes.

Awesome!

webpack
css-loader

CSS Modules is a specification that can be implemented in multiple ways.

react-css-modules
leverages the existing CSS Modules implementation webpack css-loader.

What's the Problem?

webpack css-loader itself has several disadvantages:

  • You have to use
    camelCase
    CSS class names.
  • You have to use
    styles
    object whenever constructing a
    className
    .
  • Mixing CSS Modules and global CSS classes is cumbersome.
  • Reference to an undefined CSS Module resolves to
    undefined
    without a warning.

React CSS Modules component automates loading of CSS Modules using

styleName
property, e.g.
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';

class Table extends React.Component { render () { return

A0
B0
; } }

export default CSSModules(Table, styles);

Using

react-css-modules
:
  • You are not forced to use the
    camelCase
    naming convention.
  • You do not need to refer to the
    styles
    object every time you use a CSS Module.
  • There is clear distinction between global CSS and CSS Modules, e.g.
  • You are warned when
    styleName
    refers to an undefined CSS Module (
    handleNotFoundStyleName
    option).
  • You can enforce use of a single CSS module per
    ReactElement
    (
    allowMultiple
    option).

The Implementation

react-css-modules
extends
render
method of the target component. It will use the value of
styleName
to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the
ReactElement
className
property value.

Awesome!

Usage

Setup consists of:

Module Bundler

webpack

Development

In development environment, you want to Enable Sourcemaps and webpack Hot Module Replacement (HMR).

style-loader
already supports HMR. Therefore, Hot Module Replacement will work out of the box.

Setup:

{
    test: /\.css$/,
    loaders: [
        'style-loader?sourceMap',
        'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
    ]
}
Production

In production environment, you want to extract chunks of CSS into a single stylesheet file.

Advantages:

  • Fewer style tags (older IE has a limit)
  • CSS SourceMap (with
    devtool: "source-map"
    and
    css-loader?sourceMap
    )
  • CSS requested in parallel
  • CSS cached separate
  • Faster runtime (less code and DOM operations)

Caveats:

  • Additional HTTP request
  • Longer compilation time
  • More complex configuration
  • No runtime public path modification
  • No Hot Module Replacement

– extract-text-webpack-plugin

Setup:

  • Install
    style-loader
    .
  • Install
    css-loader
    .
  • Use

    extract-text-webpack-plugin
    to extract chunks of CSS into a single stylesheet.

  • Setup

    /\.css$/
    loader:
    • ExtractTextPlugin v1x:
    {
      test: /\.css$/,
      loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
    }
    
    • ExtractTextPlugin v2x:
    {
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader?modules,localIdentName="[name]-[local]-[hash:base64:6]"'
      }),
    }
    
  • Setup

    extract-text-webpack-plugin
    plugin:
    • ExtractTextPlugin v1x:
    new ExtractTextPlugin('app.css', {
        allChunks: true
    })
    
    • ExtractTextPlugin v2x:
    new ExtractTextPlugin({
      filename: 'app.css',
      allChunks: true
    })
    

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

Browserify

Refer to

css-modulesify
.

Extending Component Styles

Use

styles
property to overwrite the default component styles.

Explanation using

Table
component:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';

class Table extends React.Component { render () { return

A0
B0
; } }

export default CSSModules(Table, styles);

In this example,

CSSModules
is used to decorate
Table
component using
./table.css
CSS Modules. When
Table
component is rendered, it will use the properties of the
styles
object to construct
className
values.

Using

styles
property you can overwrite the default component
styles
object, e.g.
import customStyles from './table-custom-styles.css';

;

Interoperable CSS can extend other ICSS. Use this feature to extend default styles, e.g.

/* table-custom-styles.css */
.table {
    composes: table from './table.css';
}

.row { composes: row from './table.css'; }

/* .cell { composes: cell from './table.css'; } */

.table { width: 400px; }

.cell { float: left; width: 154px; background: #eee; padding: 10px; margin: 10px 0 10px 10px; }

In this example,

table-custom-styles.css
selectively extends
table.css
(the default styles of
Table
component).

Refer to the

UsingStylesProperty
example for an example of a working implementation.

styles
Property

Decorated components inherit

styles
property that describes the mapping between CSS modules and CSS classes.
class extends React.Component {
    render () {
        

; } }

In the above example,

styleName='foo'
and
className={this.props.styles.foo}
are equivalent.

styles
property is designed to enable component decoration of Loops and Child Components.

Loops and Child Components

styleName
cannot be used to define styles of a
ReactElement
that will be generated by another component, e.g.
import React from 'react';
import CSSModules from 'react-css-modules';
import List from './List';
import styles from './table.css';

class CustomList extends React.Component { render () { let itemTemplate;

    itemTemplate = (name) => {
        return </pre><li stylename="item-template">{name}</li>;
    };

    return <list itemtemplate="{itemTemplate}"></list>;
}

}

export default CSSModules(CustomList, styles);

The above example will not work.

CSSModules
is used to decorate
CustomList
component. However, it is the
List
component that will render
itemTemplate
.

For that purpose, the decorated component inherits

styles
property that you can use just as a regular CSS Modules object. The earlier example can be therefore rewritten to:

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

class CustomList extends React.Component { render () { let itemTemplate;

    itemTemplate = (name) =&gt; {
        return </pre><li classname="{this.props.styles['item-template']}">{name}</li>;
    };

    return <list itemtemplate="{itemTemplate}"></list>;
}

}

export default CSSModules(CustomList, styles);

You can use

styleName
property within the child component if you decorate the child component using
CSSModules
before passing it to the rendering component, e.g.
import React from 'react';
import CSSModules from 'react-css-modules';
import List from './List';
import styles from './table.css';

class CustomList extends React.Component { render () { let itemTemplate;

    itemTemplate = (name) =&gt; {
        return </pre><li stylename="item-template">{name}</li>;
    };

    itemTemplate = CSSModules(itemTemplate, this.props.styles);

    return <list itemtemplate="{itemTemplate}"></list>;
}

}

export default CSSModules(CustomList, styles);

Decorator

/**
 * @typedef CSSModules~Options
 * @see {@link https://github.com/gajus/react-css-modules#options}
 * @property {Boolean} allowMultiple
 * @property {String} handleNotFoundStyleName
 */

/**

  • @param {Function} Component
  • @param {Object} defaultStyles CSS Modules class map.
  • @param {CSSModules~Options} options
  • @return {Function}
  • /

You need to decorate your component using

react-css-modules
, e.g.
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';



class Table extends React.Component { render () { return

A0
B0
; } }

export default CSSModules(Table, styles);

Thats it!

As the name implies,

react-css-modules
is compatible with the ES7 decorators syntax:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';

@CSSModules(styles) export default class extends React.Component { render () { return

A0
B0
; } }

Awesome!

Refer to the react-css-modules-examples repository for an example of webpack setup.

Options

Options are supplied as the third parameter to the

CSSModules
function.
CSSModules(Component, styles, options);

or as a second parameter to the decorator:

@CSSModules(styles, options);

allowMultiple

Default:

false
.

Allows multiple CSS Module names.

When

false
, the following will cause an error:

handleNotFoundStyleName

Default:

throw
.

Defines the desired action when

styleName
cannot be mapped to an existing CSS Module.

Available options:

  • throw
    throws an error
  • log
    logs a warning using
    console.warn
  • ignore
    silently ignores the missing style name

SASS, SCSS, LESS and other CSS Preprocessors

Interoperable CSS is compatible with the CSS preprocessors. To use a preprocessor, all you need to do is 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 query (loaders are processed from right to left):
{
    test: /\.scss$/,
    loaders: [
        'style',
        'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
        'resolve-url',
        'sass'
    ]
}

Enable Sourcemaps

To enable CSS Source maps, add

sourceMap
parameter to the css-loader and to the
sass-loader
:
{
    test: /\.scss$/,
    loaders: [
        'style?sourceMap',
        'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
        'resolve-url',
        'sass?sourceMap'
    ]
}

Class Composition

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

.box {
    width: 100px;
    height: 100px;
}

.empty { composes: box;

background: #4CAF50;

}

.full { composes: box;

background: #F44336;

}

Composition promotes better separation of markup and style using semantics that would be hard to achieve without CSS Modules.

Because CSS Module names are local, it is perfectly fine to use generic style names such as "empty" or "full", without "box-" prefix.

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

What Problems does Class Composition Solve?

Consider the same example in CSS and HTML:

.box {
    width: 100px;
    height: 100px;
}

.box-empty { background: #4CAF50; }

.box-full { background: #F44336; }

This pattern emerged with the advent of OOCSS. The biggest disadvantage of this implementation is that you will need to change HTML almost every time you want to change the style.

Class Composition Using CSS Preprocessors

This section of the document is included as a learning exercise to broaden the understanding about the origin of Class Composition. CSS Modules support a native method of composing CSS Modules using

composes
keyword. CSS Preprocessor is not required.

You can write compositions in SCSS using

@extend
keyword and using Mixin Directives, e.g.

Using

@extend
:
%box {
    width: 100px;
    height: 100px;
}

.box-empty { @extend %box;

background: #4CAF50;

}

.box-full { @extend %box;

background: #F44336;

}

This translates to:

.box-empty,
.box-full {
    width: 100px;
    height: 100px;
}

.box-empty { background: #4CAF50; }

.box-full { background: #F44336; }

Using mixins:

@mixin box {
    width: 100px;
    height: 100px;
}

.box-empty { @include box;

background: #4CAF50;

}

.box-full { @include box;

background: #F44336;

}

This translates to:

.box-empty {
    width: 100px;
    height: 100px;
    background: #4CAF50;
}

.box-full { width: 100px; height: 100px; background: #F44336; }

Global CSS

CSS Modules does not restrict you from using global CSS.

:global .foo {

}

However, 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 Modules

Avoid using multiple CSS Modules to describe a single element. Read about Class Composition.

That said, if you require to use multiple CSS Modules to describe an element, enable the

allowMultiple
option. When multiple CSS Modules are used to describe an element,

react-css-modules
will append a unique class name for every CSS Module it matches in the
styleName
declaration, e.g.
.button {

}

.active {

}

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

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.