Product & IT Blog
Menu

TL;DR: We implemented a library dumb-bem which helps you use BEM in React components.

Intro

There are two buzzwords in the front-end world. You’ve probably heard about both of them: BEM and React.

BEM is a methodology, that provides you with simple naming convention, and React is a JavaScript library for building user interfaces.

Combining these two approaches, help you build a complex UI, while codebase stays maintainable.

At Wimdu we take a hybrid approach using BEM naming convention and some of the SMACSS concepts.

An example of a typical component:

<header className='header header--landing'>
  <h1 className='header__title'>...</h1>
  <h2 className='header__subtitle is-hidden'>...</h2>
</header>

Here we have

Reusable components

We use ES6 JavaScript version and stateless function components for describing React components.

Let’s try to make a Header component displaying a title, a subtitle, and make it re-usable for the future.

We start with a simple React component:

// components/header/index.js

export default ({ modifier, title, subtitle }) => (
  <header className=`header header--${modifier}`>
    <h1 className='header__title'>{ title }</h1>
    <h2 className=`header__subtitle ${!subtitle ? 'is-hidden' : ''}`>
      { subtitle }
    </h2>
  </header>
)

Now we can render it with different props:

import Header from 'components/header'

// For example, we can render it on a landing page
ReactDOM.render(
  <Header
    modifier='landing'
    title='City Apartments'
    subtitle='Over 5 Million Nights Booked'
  />
, node)

// or on the About page
ReactDOM.render(
  <Header
    modifier='about'
    title='About Wimdu'
    subtitle='Meet our team and learn about what we do'
  />
, node)

Let’s make modifier optional. We may want to use classnames library for gently joining class names.

// components/header/index.js

import cx from 'classnames'

export default ({ modifier, title, subtitle }) => (
  <header className={cx('header', { [`header--${modifier}`]: modifier })}>
    <h1 className='header__title'>{ title }</h1>
    <h2 className={cx('header__subtitle', { 'is-hidden': subtitle })}>
      { subtitle }
    </h2>
  </header>
)

Quite messy and too much code to write for every new component.

Ease of use

Honestly, all we want to do in this component is simply provide a modifier for header block and place title and subtitle elements inside.

It would be great to abstract Header, Title and Subtitle elements outside of this component.

// components/header/index.js

import { Header, Title, Subtitle } from './elements'

export default ({ modifier, title, subtitle }) => (
  <Header modifier={modifier}>
    <Title>{ title }</Title>
    <Subtitle hidden={!subtitle}>{ subtitle }</Subtitle>
  </Header>
)

Let’s define Header block and two elements: Title and Subtitle.

How can we do it? The idea is to take a React element and modify its props.

We will use transform-props-with library for that:

// components/header/elements.js

import cx from 'classnames'
import tx from 'transform-props-with'

const addElementStyles = (oldProps) => {
  const { hidden, modifier, name, ...props } = oldProps

  return {
    className: cx({
      [`header__${name}`]: name,
      [`header__${name}--${modifier}`]: name && modifier,
      ['is-hidden']: hidden
    }),
    ...props
  }
}

export const Header = tx({ className: 'header' })('header')
export const Title = tx([{ name: 'title' }, addElementStyles])('h1')
export const Subtitle = tx([{ name: 'subtitle' }, addElementStyles])('h2')

So basically we have just decorated React elements header, h1 and h2.

Whenever we pass modifier or hidden props to our components — a proper class name will be generated.

Meet the dumb-bem

dumb-bem is a library we created for making atomic React components with BEM/SMACSS naming rules.

When you pass a block name to dumbBem function, it returns a decorator which you can use to transform props on React elements.

// components/header/elements.js

import dumbBem from 'dumb-bem'
import tx from 'transform-props-with'

const dumbHeader = dumbBem('header')

const Header = tx(dumbHeader)('header')
const Title = tx([dumbHeader, { element: 'title' }])('h1')
const Subtitle = tx([dumbHeader, { element: 'subtitle' }])('h2')

export default ({ modifier, title, subtitle }) => (
  <Header modifier={modifier}>
    <Title>{ title }</Title>
    <Subtitle hidden={!subtitle}>{ subtitle }</Subtitle>
  </Header>
)

It’s available on github and npm.

Read more detailed usage of this library in the future blog post.

About the author

Alexander Gudulin

Front-end Developer

Share this article