BEM CSS in React

Eric Masiello
Level Up Coding
Published in
8 min readMar 6, 2020

--

In this post, I aim to answer two questions:

  1. What is BEM?
  2. How should BEM be applied to styling React components?

BEM is an acronym for Block Element Modifier. It’s not a framework or library. It is but a small piece of a larger CSS architecture puzzle. Specifically, BEM is a strict CSS selector naming convention that provides a valuable mental model for component composition. Allow me to explain…

The term element in BEM should not be confused with an HTML element. An HTML element is the actual application of an HTML tag within an HTML document. For example, an input tag represented as an HTML element is <input />. HTML elements support attributes such as the class attribute (e.g. <input class="fancy-input" />) which we'll be utilizing heavily in our exploration of BEM.

When building user interfaces today, we’re often using component-based JavaScript libraries like React, Vue, Svelte, and the like. Different libraries and frameworks have opinions on styling. React, for better or worse, largely has zero opinion. Before I even knew what React was, I used BEM to create component libraries. Fast-forwarding several years, I’m kneedeep in the React world and still find BEM my go-to for building scalable, predictable CSS.

The BEM Block

B is for Block. Blocks are standalone components that may or may not be reused throughout your site. In a React context, A BEM block generally maps to a single React presentational component. They can be big meaty components such as a page hero filled with a splashy image, title, call to action, and navigation menu. Blocks also take the form of simple components such as buttons, links, or heading elements. BEM blocks can and often should contain other BEM blocks. If we’ve defined block components Button, Navigation, and Heading, it stands to reason that our Hero block would likely contain such components.

When styling a block, the idea is that the block should contain all the base styles necessary for that component. Variants of a block, called modifiers, will have CSS rules that modify the base block styles (we’ll touch more on this later).

Our Button component might look like this:

I prefer to name the block selector the same as the component. In this case, Button maps to the .button class name. Some folks like to capitalize the name of the block (.Button). Personally, I follow kebab case for my CSS class names but you feel free to do you 😁.

The BEM modifier

Skipping over the BEM element for a moment, I want to explain the BEM modifier. Modifiers are exactly that — modifiers. They are intended to modify a base class (typically a block). A modifier should only include the styles that are necessary to modify the base class. Modifiers use a double dash -- to separate the modifier name from the base class name they modify:

BEM button with modifiers

In the above code snippet, we’ve introduced 3 new modifiers: outline, primary, and outline-primary. These modifiers are for illustrative purposes but they highlight two important points I want to make about modifiers:

  1. Modifiers should be declared after the thing they are modifying. This is necessary because the CSS specificity for both .button and all its modifiers are the same. However, since the modifiers appear after the base class, the modifier’s rules trump and conflicting rules defined in the base class.
  2. Modifiers must only include the specific rules they need to modify. For example, the outline modifier only has 3 rules: background-color, border-color, and color. It doesn't include anything about font-size, font-weight etc. Those are left for the base class .button to define. This means that when applying a modifier to an HTML element, you must include the base class as well.
How to and how to NOT apply BEM modifiers

An alternative, ill-advised approach

You might look at the above example and find the redundant BEM block namespace awkward and wonder why you wouldn’t do something like this:

When applied to HTML, it would change to the following:

On the upside, this approach means less to write and we’ve effectively namespaced our modifiers to the base class by coupling the selectors (e.g. .button.outline). The reason I do not recommend this approach speaks to a larger but related belief of mine of CSS architecture: CSS selectors should be least specific as possible.

The problem with the .button.outline {...} example is that we've doubled up the specificity of the selector. This can create problems if I later come along and want to further modify either the color, background-color, or border-color for a specific use case. In order for my new styles to work, my new selector must be at least as specific as .button.outline.

In this case, the .flashy-button border color will be immediately trumped by the more specific .button.outline selector. To fix this, we’ll need to introduce any number of hacks like doubling up the class name (.flashy-button.flashy-button {...}) or throw an !important on there. We can avoid this problem by keeping our CSS specificity graph relatively flat by using a single class name for modifiers.

Applying modifiers to React

I’m a big fan of the classnames library. It's a useful way to conditionally apply class names to React elements. Let's add support for our 3 modifiers to our Button component:

The classnames library can take any number of strings or objects as arguments. String arguments are concatenated to the resulting string that the classNames function returns. Objects are used to conditionally apply class names. The key of the object represents the class name you may want to add. The corresponding value is an expression that evaluates to true or false. If the expression evaluates to true, the class name stored in the key of the object is applied to the resulting string that classNames returns. If we were to render our Button component with the following props:

The classNames function will return 'button button--outline-primary'.

The BEM element

In my experience, BEM elements are the most misunderstood part of the BEM equation. BEM elements are expressed with a __ (double underscore). The structure follows ..<block-name>__<child-name>. BEM elements are namespaced by their parent block. The tight relationship between a BEM block and BEM element is important. However, BEM element should only be used if styling an HTML element only makes sense within the context of the wrapping BEM block. To illustrate this, I'll use a more elaborate BEM block example: a hero. Our React component might start by looking like this:

In this case, the BEM block is hero and the aptly named hero class name is correctly applied to the top-level element: the header. But how should I style the child elements: the h1, p, and Button? Should I immediately create a .hero__title, .hero__message, and .hero__button? Maybe. But not necessarily.

Let’s start with the h1 and the p. Regardless of all this BEM talk, it’s wise to provide some baseline element styling for common elements like your headings, paragraphs, etc. As an example, I would include this before any of my component styles.

In the context of the hero block, if the baseline styles applied to these elements are fine, then you can leave them be. The same is true for our custom Button component. If it looks how you want, then it will be styled with its own BEM block class name .button and we can call it a day.

However, what I often see developers do is immediately jump to styling all block ancestor elements as BEM elements. So instead they do this:

In the worst of cases, a developer might completely ignore our reusable Button and create a net new set of styles for .hero__button that duplicate many styles in the block .button. This defeats the point of reusable components so don’t do this.

So again, if everything looks good and no further styling is needed, we can leave our hero block alone without creating any BEM element styles. However, if we need to tweak a few things that are specific to our hero use case, then its time to introduce BEM elements:

What’s interesting about this is that the Button is serving as both a BEM block (.button) and a BEM element (.hero__cta). That’s totally fine! As far as I’m concerned, that’s CSS composition at its finest 🤓

There is one tiny wrinkle with our Button component. The Hero component is attempting to pass down a custom className prop to our Button. If we use the existing implementation of Button, Button will ignore this prop. Let’s fix that:

We must destructure the className prop from the props object and then add it to the arguments of classNames() . If a value is passed to the className prop, it will be appropriately applied to the resulting classes variable. If it is omitted, the classNames function will omit the undefined value form the result.

A note on CSS specificity

If you look back at our hero example where we introduced some BEM element styles, you’ll note that the BEM element hero__cta was applied to the Button element. We know that under the hood, the Button will then apply both the button and hero__cta class names to the rendered <button />. .button and .hero__cta carry the same level of specificity. Our intent is for the border color applied to Button be the one specified in .hero__cta and not the baseline value defined in .button. How do we make sure that happens?

This is where your bundler comes in to play. Pay attention to the order in which we are loading the ./Button and ./Hero.css dependencies in the Hero.js module:

./Hero.css is imported after ./Button. This is important. When your bundler (e.g. Webpack) builds the final bundles (both JavaScript and CSS), it does so by paying close attention to the order in which dependencies are authored. Because ./Button is imported before ./Hero.css , Webpack (and presumably other bundlers) will step into the Button.js first before even paying any mind to ./Hero.css. And since Button.js imports ./Button.css, Button.css will be included in the resulting CSS bundle before the contents of Hero.css. That is why I always recommend that you load your component’s CSS dependency as the final dependency in your module.

In conclusion

There’s plenty of ways to author CSS today. There’s a number of CSS-in-JS options, CSS modules, and then more “traditional” ones like what I’ve touched on in this article. In my opinion, CSS-in-JS shines in cases where styles are highly dynamic based on data (props). But in instances where all you need are a few simple variations and a sane way to model and scale your site/application’s CSS, BEM has proven a terrific scalable model.

Final note: I am also a big fan of CSS modules. In a future article, I’ll talk about how I apply the BEM methodology to CSS modules.

--

--