Skip to content

Transforms styleName to className using compile time CSS module resolution.

License

Notifications You must be signed in to change notification settings

gajus/babel-plugin-react-css-modules

Repository files navigation

babel-plugin-react-css-modules

Travis build status NPM version Canonical Code Style Twitter Follow

Transforms styleName to className using compile time CSS module resolution.

In contrast to react-css-modules, babel-plugin-react-css-modules has a loot smaller performance overhead (0-10% vs +50%; see Performance) and a lot smaller size footprint (less than 2kb vs 17kb reaact-css-modules + lodash dependency).

Background

react-css-modules introduced a convention of using styleName attribute to reference CSS module. react-css-modules is a higher-order React component. It is using the styleName value to construct the className at the run-time. This abstraction frees a developer from needing to reference the imported styles object when using CSS modules (What's the problem?). However, this approach has a measurable performance penalty at the cost of better developer experience (DX).

babel-plugin-react-css-modules solves the DX problem without impacting the performance.

Performance

The important metric here is "Difference from base" (DFB). "base" is defined as using React with hardcoded className values. The lesser the DFB value, the bigger the performance impact.

Note: This benchmark suite does not include a scenario when babel-plugin-react-css-modules can statically construct the value of className. If a literal value of the className is constructed at the compile time, the performance is equal to the base benchmark.

Name Operations per second (relative margin of error) Sample size Difference from the base benchmark
Using className (base) 9551 (±1.47%) 587 -0%
react-css-modules 5914 (±2.01%) 363 -61%
babel-plugin-react-css-modules (runtime, anonymous) 9145 (±1.94%) 540 -4%
babel-plugin-react-css-modules (runtime, named) 8786 (±1.59%) 527 -8%

Platform info:

  • Darwin 16.1.0 x64
  • Node.JS 7.1.0
  • V8 5.4.500.36
  • NODE_ENV=production
  • Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz × 8

View the ./benchmark.

Run the benchmark:

git clone git@github.com:gajus/babel-plugin-react-css-modules.git
cd ./babel-plugin-react-css-modules
npm install
npm run build
cd ./benchmark
npm install
NODE_ENV=production ./test

How does it work?

  1. Builds index of all stylesheet imports per file.
  2. Uses postcss to parse the matching CSS files.
  3. Iterates through all JSX element declarations.
  4. Uses the styleName value to resolve the generated CSS class name of the CSS module.
  • If styleName value is a string literal, generates a string literal value.
  • If styleName value is non-string (variable, condition, etc.), uses a helper function to construct className value at the runtime.
  1. Removes the styleName attribute from the element.
  2. Appends the resulting className to the existing className value (or creates className attribute if one does not exist).

Configuration

Name Description Default
generateScopedName Refer to Generating scoped names N/A (delegates default resolution to postcss-modules)

Missing a configuration? Raise an issue.

Conventions

Named reference

Named reference is used to refer to a specific stylesheet import.

Format: [name of the import].[CSS module name].

Example:

import foo from './foo1.css';
import bar from './bar1.css';

// Imports "a" CSS module from ./foo1.css.
<div styleName="foo.a"></div>;

// Imports "a" CSS module from ./bar1.css.
<div styleName="bar.a"></div>;

Example transpilations

Anonymous styleName resolution

When styleName is a literal string value, babel-plugin-react-css-modules resolves the value of className at the compile time.

Input:

import './bar.css';

<div styleName="a"></div>;

Output:

import './bar.css';

<div className="bar___a"></div>;

Named styleName resolution

When file imports multiple stylesheets, you must use a named reference.

Input:

import foo from './foo1.css';
import bar from './bar1.css';

<div styleName="foo.a"></div>;
<div styleName="bar.a"></div>;

Output:

import foo from './foo1.css';
import bar from './bar1.css';

<div className="foo___a"></div>;
<div className="bar___a"></div>;

Runtime styleName resolution

When the value of styleName cannot be determined at the compile time, babel-plugin-react-css-modules inlines all possible styles into the file. It then uses getClassName helper function to resolve the styleName value at the runtime.

Input:

import './bar.css';

<div styleName={Math.random() > .5 ? 'a' : 'b'}></div>;

Output:

import _getClassName from 'babel-plugin-react-css-modules/dist/browser/getClassName';
import foo from './bar.css';

const _styleModuleImportMap = {
  foo: {
    a: 'bar__a',
    b: 'bar__b'
  }
};

<div styleName={_getClassName(Math.random() > .5 ? 'a' : 'b', _styleModuleImportMap)}></div>;

Limitations

Have a question or want to suggest an improvement?