Using BEM Classes

The BEM methodology was created at Yandex for developing websites that need to be built quickly and maintained for many years. It allows creating scalable and reusable interface components.

You can read more about the approach in the official documentation

In this article, we will look at how to implement this approach using built-in tools.

Basic Usage

To work with classes in Steroids, use the useBem hook imported from @steroidsjs/core/hooks.

As the only argument, it takes the name of the BEM block.

Block

To create a BEM block, use the block method of the useBem hook.

export default function NavBarTemplate() {
    const bem = useBem('NavBarTemplate');

    return (
        <div className={bem.block()}> // will be replaced with NavBarTemplate
            // Rest of the code
        </div>
    );
}

Element

To create a BEM element, use the element method of the useBem hook.

Basic usage example:

export default function NavBarTemplate() {
    const bem = useBem('NavBarTemplate');

    return (
        <div className={bem.block()}> // NavBarTemplate
            <div className={bem.element('logo')}> // NavBarTemplate__logo
                // Logo
            </div>

            <a href='#' className={bem.element('link')}> // NavBarTemplate__link
                // Link to home
            </a>

            // Rest of the code
        </div>
    );
}

BEM Block Modifier

To create a BEM block modifier, pass the modifier as an argument to the block method.

export default function CustomButton() {
    const bem = useBem('CustomButton');

    return (
        <button className={bem.block('red')}> // CustomButton CustomButton_red
            // Rest of the code
        </button>
    );
}

Note that both the BEM block class and the BEM modifier class will be applied.

BEM Element Modifier

To create a BEM element modifier, pass the modifier as the second argument to the element method.

    <div className={bem.block()}> // NavBarTemplate
        <div className={bem.element('logo', 'big')}> // NavBarTemplate__logo NavBarTemplate__logo_big
            // Logo 
        </div>
    </div>

Multiple and Conditional Modifiers

An object can be used as a modifier, where the keys are the modifiers and the values are the conditions under which the modifiers will or will not be applied.

    <button 
        className={bem.block({ // The block class will always be applied (e.g., CustomButton)
            hidden: props.isHidden, // if props.isHidden === true, the CustomButton_hidden class will be applied
        })}
    > 
        <span 
            className={bem.element('content', { // The CustomButton__content class will always be applied
                firstLine: true, // the CustomButton__content_firstLine class will always be applied
                dark: theme === 'dark' // if theme === 'dark', the CustomButton__content__dark class will be applied
            })}
            > 
                Submit form
        </span>
    </div>

The value in the modifier object can be not only boolean, but also string or number.

<div className={bem.element('buttons')}>
    {['one', 'two', 'three'].map((value, index) => (
        <div
            key={index}
            className={bem.element('button', {
                number: index,
            })}
        >
            {value}
        </div>
    ))}
</div>

// transforms into the markup:

<div class="NavBar__buttons">
    <div class="NavBar__button">one</div>
    <div class="NavBar__button NavBar__button_number_1">two</div>
    <div class="NavBar__button NavBar__button_number_2">three</div>
</div>

Combining Classes

If you need to combine multiple classes on one element (for example, when you need to pass styles from the parent to a custom button component), you can pass the necessary classes.

// The result of calling the bem.element('home', 'active') function in the parent element is passed as additionalClassNames
export default function CustomButton(props: {additionalClassNames: string}) {
    const bem = useBem('CustomButton');

    return (
        <button className={bem(
            bem.block(), 
            props.additionalClassNames
            )}> // CustomButton NavBarTemplate__home NavBarTemplate__home_active
            // Rest of the code
        </button>
    );
}

Summary

const bem = useBem('Block');

bem.block(); // Block
bem.block('modificator'); // Block Block_modificator
bem.block({
    modificator: false,
    hidden: true,
    button: 'one',
}); // Block Block_hidden Block_button_one


bem.element('Element'); // Block__Element
bem.element('Element', 'modificator'); // Block__Element Block__Element_modificator
bem.element('Element', {
    modificator: true,
    hidden: false,
    button: 'two',
}); // Block__Element Block__Element_modificator Block__Element_button_one

bem(
    bem.block(), 
    bem.element('FirstElement'), 
    bem.element('SecondElement', 'modificator'), 
    'visuallyHidden'
); // Block Block__FirstElement Block__SecondElement Block__SecondElement_modificator visuallyHidden