Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork215
Seamless mapping of class names to CSS modules inside of React components.
License
gajus/react-css-modules
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
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!
If you are considering to use
react-css-modules, evaluate ifbabel-plugin-react-css-modulescovers your use case.babel-plugin-react-css-modulesis a lightweight alternative ofreact-css-modules.
babel-plugin-react-css-modulesis not a drop-in replacement and does not cover all the use cases ofreact-css-modules.However, it has a lot smaller performance overhead (0-10% vs +50%; seePerformance) and a lot smaller size footprint (less than 2kb vs +17kb).It is easy to get started! See the demohttps://github.com/gajus/babel-plugin-react-css-modules/tree/master/demo
- CSS Modules
- What's the Problem?
- The Implementation
- Usage
- SASS, SCSS, LESS and other CSS Preprocessors
- Class Composition
- Global CSS
- Multiple CSS Modules
CSS Modules are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such aswebpack 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:
importReactfrom'react';importstylesfrom'./table.css';exportdefaultclassTableextendsReact.Component{render(){return<divclassName={styles.table}><divclassName={styles.row}><divclassName={styles.cell}>A0</div><divclassName={styles.cell}>B0</div></div></div>;}}
Rendering the component will produce a markup similar to:
<divclass="table__table___32osj"><divclass="table__row___2w27N"><divclass="table__cell___1oVw5">A0</div><divclass="table__cell___1oVw5">B0</div></div></div>
and a corresponding CSS file that matches those CSS classes.
Awesome!
CSS Modules is a specification that can be implemented in multiple ways.react-css-modules leverages the existing CSS Modules implementation webpackcss-loader.
webpackcss-loader itself has several disadvantages:
- You have to use
camelCaseCSS class names. - You have to use
stylesobject whenever constructing aclassName. - Mixing CSS Modules and global CSS classes is cumbersome.
- Reference to an undefined CSS Module resolves to
undefinedwithout a warning.
React CSS Modules component automates loading of CSS Modules usingstyleName property, e.g.
importReactfrom'react';importCSSModulesfrom'react-css-modules';importstylesfrom'./table.css';classTableextendsReact.Component{render(){return<divstyleName='table'><divstyleName='row'><divstyleName='cell'>A0</div><divstyleName='cell'>B0</div></div></div>;}}exportdefaultCSSModules(Table,styles);
Usingreact-css-modules:
- You are not forced to use the
camelCasenaming convention. - You do not need to refer to the
stylesobject every time you use a CSS Module. - There is clear distinction between global CSS and CSS Modules, e.g.
<divclassName='global-css'styleName='local-module'></div>
- You are warned when
styleNamerefers to an undefined CSS Module (handleNotFoundStyleNameoption). - You can enforce use of a single CSS module per
ReactElement(allowMultipleoption).
react-css-modules extendsrender method of the target component. It will use the value ofstyleName to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to theReactElementclassName property value.
Setup consists of:
- Setting up amodule bundler to load theInteroperable CSS.
- Decorating your component using
react-css-modules.
In development environment, you want toEnable Sourcemaps and webpackHot Module Replacement (HMR).style-loader already supports HMR. Therefore, Hot Module Replacement will work out of the box.
Setup:
- Install
style-loader. - Install
css-loader. - Setup
/\.css$/loader:
{test:/\.css$/,loaders:['style-loader?sourceMap','css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]']}
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"andcss-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
Setup:
Install
style-loader.Install
css-loader.Use
extract-text-webpack-pluginto 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-pluginplugin:ExtractTextPlugin v1x:
newExtractTextPlugin('app.css',{allChunks:true})
ExtractTextPlugin v2x:
newExtractTextPlugin({filename:'app.css',allChunks:true})
Refer towebpack-demo orreact-css-modules-examples for an example of a complete setup.
Refer tocss-modulesify.
Usestyles property to overwrite the default component styles.
Explanation usingTable component:
importReactfrom'react';importCSSModulesfrom'react-css-modules';importstylesfrom'./table.css';classTableextendsReact.Component{render(){return<divstyleName='table'><divstyleName='row'><divstyleName='cell'>A0</div><divstyleName='cell'>B0</div></div></div>;}}exportdefaultCSSModules(Table,styles);
In this example,CSSModules is used to decorateTable component using./table.css CSS Modules. WhenTable component is rendered, it will use the properties of thestyles object to constructclassName values.
Usingstyles property you can overwrite the default componentstyles object, e.g.
importcustomStylesfrom'./table-custom-styles.css';<Tablestyles={customStyles}/>;
Interoperable CSS canextend 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:10px010px10px;}
In this example,table-custom-styles.css selectively extendstable.css (the default styles ofTable component).
Refer to theUsingStylesProperty example for an example of a working implementation.
Decorated components inheritstyles property that describes the mapping between CSS modules and CSS classes.
classextendsReact.Component{render(){<div><pstyleName='foo'></p><pclassName={this.props.styles.foo}></p></div>;}}
In the above example,styleName='foo' andclassName={this.props.styles.foo} are equivalent.
styles property is designed to enable component decoration ofLoops and Child Components.
styleName cannot be used to define styles of aReactElement that will be generated by another component, e.g.
importReactfrom'react';importCSSModulesfrom'react-css-modules';importListfrom'./List';importstylesfrom'./table.css';classCustomListextendsReact.Component{render(){letitemTemplate;itemTemplate=(name)=>{return<listyleName='item-template'>{name}</li>;};return<ListitemTemplate={itemTemplate}/>;}}exportdefaultCSSModules(CustomList,styles);
The above example will not work.CSSModules is used to decorateCustomList component. However, it is theList component that will renderitemTemplate.
For that purpose, the decorated component inheritsstyles property that you can use just as a regular CSS Modules object. The earlier example can be therefore rewritten to:
importReactfrom'react';importCSSModulesfrom'react-css-modules';importListfrom'./List';importstylesfrom'./table.css';classCustomListextendsReact.Component{render(){letitemTemplate;itemTemplate=(name)=>{return<liclassName={this.props.styles['item-template']}>{name}</li>;};return<ListitemTemplate={itemTemplate}/>;}}exportdefaultCSSModules(CustomList,styles);
You can usestyleName property within the child component if you decorate the child component usingCSSModules before passing it to the rendering component, e.g.
importReactfrom'react';importCSSModulesfrom'react-css-modules';importListfrom'./List';importstylesfrom'./table.css';classCustomListextendsReact.Component{render(){letitemTemplate;itemTemplate=(name)=>{return<listyleName='item-template'>{name}</li>;};itemTemplate=CSSModules(itemTemplate,this.props.styles);return<ListitemTemplate={itemTemplate}/>;}}exportdefaultCSSModules(CustomList,styles);
/** *@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 usingreact-css-modules, e.g.
importReactfrom'react';importCSSModulesfrom'react-css-modules';importstylesfrom'./table.css';classTableextendsReact.Component{render(){return<divstyleName='table'><divstyleName='row'><divstyleName='cell'>A0</div><divstyleName='cell'>B0</div></div></div>;}}exportdefaultCSSModules(Table,styles);
Thats it!
As the name implies,react-css-modules is compatible with theES7 decorators syntax:
importReactfrom'react';importCSSModulesfrom'react-css-modules';importstylesfrom'./table.css';@CSSModules(styles)exportdefaultclassextendsReact.Component{render(){return<divstyleName='table'><divstyleName='row'><divstyleName='cell'>A0</div><divstyleName='cell'>B0</div></div></div>;}}
Refer to thereact-css-modules-examples repository for an example of webpack setup.
Options are supplied as the third parameter to theCSSModules function.
CSSModules(Component,styles,options);
or as a second parameter to the decorator:
@CSSModules(styles,options);
Default:false.
Allows multiple CSS Module names.
Whenfalse, the following will cause an error:
<divstyleName='foo bar'/>
Default:throw.
Defines the desired action whenstyleName cannot be mapped to an existing CSS Module.
Available options:
throwthrows an errorloglogs a warning usingconsole.warnignoresilently ignores the missing style name
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 installingsass-loader and adding!sass to the end of thestyle-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']}
To enable CSS Source maps, addsourceMap parameter to the css-loader and to thesass-loader:
{test:/\.scss$/,loaders:['style?sourceMap','css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]','resolve-url','sass?sourceMap']}
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 aboutCSS Modules and the officialspec of the CSS Modules.
Consider the same example in CSS and HTML:
.box {width:100px;height:100px;}.box-empty {background:#4CAF50;}.box-full {background:#F44336;}
<divclass='box box-empty'></div>
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.
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 usingcomposes keyword. CSS Preprocessor is not required.
You can write compositions in SCSS using@extend keyword and usingMixin 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;}
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).
Avoid using multiple CSS Modules to describe a single element. Read aboutClass Composition.
That said, if you require to use multiple CSS Modules to describe an element, enable theallowMultiple 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 thestyleName declaration, e.g.
.button {}.active {}
<divstyleName='button active'></div>
This will map bothInteroperable CSS CSS classes to the target element.
About
Seamless mapping of class names to CSS modules inside of React components.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
