Core library for developing with React-like libs and BEM methodology
Clone or download
Latest commit 6d33721 Aug 13, 2018

README.md

Bem React Core

A library for developing user interfaces using the BEM methodology in React. BEM React Core supports TypeScript and Flow type annotations.

Installation

Using npm:

npm init
npm install --save bem-react-core react react-dom

Using Yarn:

yarn init
yarn add bem-react-core react react-dom

Quick start

This example shows how to create an application that will show the user a dialog box with the message "Hello, World!" when clicked.

A quick way to deploy a React project from scratch and start working with bem-react-core is by using the BEM React Boilerplate utility.

Hello, World

Install bem-react-boilerplate

git clone git@github.com:bem/bem-react-boilerplate.git bem-in-react
cd bem-in-react
npm install
npm start

src/index.tsx content

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Block } from 'bem-react-core';

class Button extends Block {
    block = 'Button';

    tag() {
        return 'button';
    }

    handleClick = () => {
        alert('Hello, World!');
    }

    attrs() {
        return {
            onClick: this.handleClick
        };
    }
}

ReactDOM.render(
    <Button>Click me</Button>,
    document.getElementById('root')
);

Then open localhost:3000 to see your app.

To create more complex projects in bem-react-core, take a look at the Basics and the API reference sections.

Basics

Creating blocks

A block is a functionally independent component of the user interface that can be reused. To create a block, you need to import the Block class from the bem-react-core library. This is the base class for creating block instances.

Example:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Block } from 'bem-react-core';

class Button extends Block {
    block = 'Button';

    tag() {
        return 'button';
    }
}

ReactDOM.render(
    <Button>Click me</Button>,
    document.getElementById('root')
);

Result:

<button class="Button">Click me</button>

Creating elements

An element is a part of a block that cannot be used without the block itself. To create an element, you need to import the Elem class from the bem-react-core library. This is the base class for creating element instances.

Example:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Block, Elem } from 'bem-react-core';

interface IButtonProps {
    children: string;
}

// Creating the Text element
class Text extends Elem {
    block = 'Button';
    elem = 'Text';

    tag() {
        return 'span';
    }
}

// Creating the Button block
class Button extends Block<IButtonProps> {
    block = 'Button';

    tag() {
        return 'button';
    }

    content() {
        return (
            <Text>{this.props.children}</Text>
        );
    }
}

ReactDOM.render(
    <Button>Click me</Button>,
    document.getElementById('root')
);

Result:

<button class="Button">
    <span class="Button-Text">Click me</span>
</button>

Creating modifiers

Modifiers define the appearance, state, or behavior of a block or element. To modify a block or element, use the HOC function withMods() from the bem-react-core library. The withMods() function gets a base block or element with a list of its modifiers as arguments, and returns a modified block or element.

Example:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Block, Elem, withMods } from 'bem-react-core';

interface IButtonProps {
    children: string;
}

interface IModsProps extends IButtonProps {
    type: 'link' | 'button';
}

// Creating the Text element
class Text extends Elem {
    block = 'Button';
    elem = 'Text';

    tag() {
        return 'span';
    }
}

// Creating the Button block
class Button<T extends IModsProps> extends Block<T> {
    block = 'Button';

    tag() {
        return 'button';
    }

    mods() {
        return {
            type: this.props.type
        };
    }

    content() {
        return (
            <Text>{this.props.children}</Text>
        );
    }
}

// Extending functionality of the Button block when the "type" property is set to the value "link"
class ButtonLink extends Button<IModsProps> {
    static mod = ({ type }: IModsProps) => type === 'link';

    tag() {
        return 'a';
    }

    mods() {
        return {
            type: this.props.type
        };
    }

    attrs() {
        return {
            href: 'www.yandex.ru'
        };
    }
}

// Combining the Button and ButtonLink classes
const ButtonView = withMods(Button, ButtonLink);

ReactDOM.render(
    <React.Fragment>
        <ButtonView type="button">Click me</ButtonView>
        <ButtonView type="link">Click me</ButtonView>
    </React.Fragment>,
    document.getElementById('root')
);

Result:

<button class="Button Button_type_button">
    <span class="Button-Text">Click me</span>
</button>
<a href="www.yandex.ru" class="Button Button_type_link">
    <span class="Button-Text">Click me</span>
</a>

Creating additional HTML markup

To create an additional HTML element with the name of the CSS class formed using the BEM methodology, you need to import the Bem helper from the bem-react-core library.

Note: Learn more about generating CSS classes.

Example:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Bem } from 'bem-react-core';

ReactDOM.render(
    <React.Fragment>
        <Bem block="MyBlock" />
        <Bem block="MyBlock" elem="Inner" />
        <Bem block="MyBlock" tag="span" />
        <Bem block="MyBlock" mods={{theme: 'default'}} />
    </React.Fragment>,
    document.getElementById('root')
);

Result:

<div class="MyBlock"></div>
<div class="MyBlock-Inner"></div>
<span class="MyBlock"></span>
<div class="MyBlock MyBlock_theme_default"></div>

Working with CSS

Generating CSS classes

Classes are generated using the fields block, elem and method mods() in accordance with the React naming scheme for CSS classes that is shown below. Separators of block, element, and modifier names are generated automatically.

React scheme for forming CSS class names:

BlockName-ElemName_modName_modVal

Note: Learn more about CSS class naming schemes.

Example:

// Creating a block
class Button extends Block {
    block = 'Button';
}

// Creating an element
class Text extends Elem {
    block = 'Button';
    elem = 'Text';
}

// Creating a block modifier
class Button extends Block {
    block = 'Button';

    mods() {
        return {
            theme: 'default'
        };
    }
}

// Creating an element modifier
class Text extends Elem {
    block = 'Button';
    elem = 'Text';

    elemMods() {
        return {
            theme: 'default'
        };
    }
}

Result:

<!-- Block -->
<div class="Button"></div>
<!-- Element -->
<div class="Button-Text"></div>
<!-- Block modifier -->
<div class="Button Button_theme_default"></div>
<!-- Element modifier -->
<div class="Button-Text Button-Text_theme_default"></div>

API reference

For more information about the API, see: REFERENCE.md.

Migrating to the API v2.0

For detailed instructions on switching to v2.0 of the API, see: MIGRATION.md.

API versions

The API is versioned according to Semantic Versioning. We recommend using the latest stable version of the library.

Note: The history of API edits can be found in CHANGELOG.md. To learn about switching between different versions of the API, see: MIGRATION.md.

Contribute

Bem React Core is an open source library that is under active development and is also used within Yandex.

If you have suggestions for improving the API, you can send us a Pull Request.

If you found a bug, you can create an issue describing the problem.

For a detailed guide to making suggestions, see: CONTRIBUTING.md.

License

© 2018 Yandex. Code released under Mozilla Public License 2.0.