@peterjwest/react-bem-classes

Decorator/utility function to expand BEM classes in React elements

Usage no npm install needed!

<script type="module">
  import peterjwestReactBemClasses from 'https://cdn.skypack.dev/@peterjwest/react-bem-classes';
</script>

README

react-bem-classes npm version build status coverage status

A utility for writing more concise BEM style classes in React.

Instead of writing this:

import React from 'react';

class Component extends React.Component {
  render() {
    const name = 'Anna', age = 42;
    return <section className="User User-active">
      <ul className="User_details">
        <li className="User_name">
          Name: <span className="User_name_smallText User_name_smallText-highlight">{name}</span>
        </li>
        <li className="User_age italic">Age: {age}</li>
      </ul>
      <form className="User_details_actions">
        <button className="User_details_action">Poke</button>
      </form>
    </section>;
  }
}

You can write this:

import React from 'react';
import { expandClasses } from 'react-bem-classes';

class Component extends React.Component {
  render() {
    const name = 'Anna', age = 42;
    return expandClasses(<section className="User &-active">
      <ul className="&_details">
        <li className="&&_name">
          Name: <span className="&_smallText &-highlight">{name}</span>
        </li>
        <li className="&&_age italic">Age: {age}</li>
      </ul>
      <form className="&_actions">
        <button className="&_action">Poke</button>
      </form>
    </section>);
  }
}

The current block and any elements are prepended onto each class with a selector character &. Using two selector characters (e.g. &&) ignores the last current element, which allows more flexible nesting (You can use more selector characters to ignore more elements).

By default this uses a slightly unusual version of BEM, assuming camel case classes, which is more concise and more legible in my opinion.

If you want to use the original BEM style you can:

import React from 'react';
import { expandClasses } from 'react-bem-classes';

const expandClassesClassic = (input) => expandClasses(input, { element: '__', modifier: '--' });

class Component extends React.Component {
  render() {
    const name = 'Anna', age = 42;
    return expandClassesClassic(<section className="User &--active">
      <ul className="&__details">
        <li className="&&__name">
          Name: <span className="&__small-text &--highlight">{name}</span>
        </li>
        <li className="&&__age italic">Age: {age}</li>
      </ul>
    </section>);
  }
}

You can also change the selector character:

import React from 'react';
import { expandClasses } from 'react-bem-classes';

class Component extends React.Component {
  render() {
    const name = 'Anna', age = 42;
    return expandClasses(<section className="User %-active">
      <ul className="%_details">
        <li className="%%_name">
          Name: <span className="%_smallText %-highlight">{name}</span>
        </li>
        <li className="%%_age italic">Age: {age}</li>
      </ul>
    </section>, { selector: '%' });
  }
}

If you're using Typescript, you can instead use the decorator:

import React from 'react';
import expandClasses from 'react-bem-classes';

class Component extends React.Component {
  @expandClasses({ selector: '%' })
  render() {
    const name = 'Anna', age = 42;
    return <section className="User %-active">
      <ul className="%_details">
        <li className="%%_name">
          Name: <span className="%_smallText %-highlight">{name}</span>
        </li>
        <li className="%%_age italic">Age: {age}</li>
      </ul>
    </section>;
  }
}

You can also use the utility with CommonJS and without JSX support:

const { createElement, Component } = require('react');
const { expandClasses } = require('react-bem-classes');

class SomeComponent extends Component {
  render() {
    const name = 'Anna', age = 42;
    return expandClasses(
      createElement('section', { className: 'User %-active' },
        createElement('ul', { className: '%_details' },
          createElement('li', { className: '%%_name' }, [
            'Name: ',
            createElement('span', { className: '%_smallText %-highlight' }),
          ]),
          createElement('li', { className: '%%_age_' }, `Age: ${age}`),
        ),
      ),
      { selector: '%' }
    );
  }
}