It is a library for declaration React components as BEM entities. It works on top of usual React-components and provides API for declaration of blocks, elements and their modifiers. Blocks and elements created with this library are fully compatible with any React components: blocks and elements can use any other React components inside and can be used inside other React components.
If you already use i-bem.js and you want to get benefits from React approach and not to lose usual BEM terms and declarative style.
If you already use React and you want to get benefits from BEM methodology.
Your code will look better without unnecessary syntax noise.
import React from 'react';
export default class Button extends React.Component {
render() {
const { size, theme, children } = this.props;
return (
<button className={`Button Button_size_${size} Button_theme_${theme}`}>
{children}
</button>
);
}
};
import { decl } from 'bem-react-core';
export default decl({
block : 'Button',
tag: 'button',
mods({ size, theme }) {
return { size, theme };
}
});
NB You can use other libraries for CSS classes generation:
Modifier is the one of key-concept of BEM methodology. Modifiers are supposed to help you make variations of the same component. bem-react-core
enables you to declare additional behaviour for modifiers easily (see more in documentation).
import React from 'react';
export default class Button extends React.Component {
render() {
const { size, theme, children } = this.props;
let className = 'Button',
content = [children];
if(size === 'large') {
className += `Button_size_${size}`;
content.unshift('Modification for size with value \'large\'.');
}
if(theme === 'primary') {
className += `Button_theme_${theme}`;
content.unshift('Modification for theme with value \'primary\'.');
}
return (
<button className={className}>
{content}
</button>
);
}
};
Usually declaration of additional behaviour requires extra conditions in main code. As a different way you could use inheritance, but it's awkward to compose many modifiers of one component at the same time.
// Button.js
import { decl } from 'bem-react-core';
export default decl({
block : 'Button',
tag: 'button',
mods({ size, theme }) {
return { size, theme };
}
});
// Button_size_large.js
import { declMod } from 'bem-react-core';
export default declMod({ size : 'large' }, {
block : 'Button',
content() {
return [
'Modification for size with value \'large\'.',
this.__base(...arguments)
];
}
});
// Button_theme_primary.js
import { declMod } from 'bem-react-core';
export default declMod({ theme : 'primary' }, {
block : 'Button',
content() {
return [
'Modification for theme with value \'primary\'.',
this.__base(...arguments)
];
}
});
NB bem-react-core
uses Inherit library for declaration. Unlike ES2015 classes it adds ability to dynamically create and modify JS class. It also helps to make super-call (this.__base(...arguments)
) without specifying method name (super.content(...arguments)
).
Redefinition levels it's a part of BEM methodology which helps you to separate and reuse your code. For example you can separate your code by platforms. bem-react-core
helps to declare React components on the different levels.
// common.blocks/Button/Button.js
import { decl } from 'bem-react-core';
export default decl({
block : 'Button',
tag : 'button',
attrs({ type }) {
return { type };
}
});
// decktop.blocks/Button/Button.js
import { decl } from 'bem-react-core';
export default decl({
block : 'Button',
willInit() {
this.state = {};
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
},
mods() {
return { hovered : this.state.hovered };
}
attrs({ type }) {
return {
...this.__base(...arguments),
onMouseEnter : this.onMouseEnter,
onMouseLeave : this.onMouseLeave
};
},
onMouseEnter() {
this.setState({ hovered : true });
},
onMouseLeave() {
this.setState({ hovered : false });
}
});
Due to this you can configure build process for bundles split by platforms.
Files from desktop.blocks
are going to common.blocks + desktop.blocks
for desktop browsers and not to common.blocks + touch.blocks
for mobile.
For comfortable usage BEM blocks and elements without declaration of JS class you can use Bem
helper in JSX.
import React from 'react';
export default ({ size, theme, tabIndex }) => (
<button className={`Button Button_size_${size} Button_theme_${theme}`} tabIndex={tabIndex}>
<span className="Button-Text">Go!</span>
</button>
);
import Bem from 'bem-react-core';
export default ({ size, theme }) => (
<Bem block="Button" mods={{ size, theme }} tag="button" attrs={{ tabIndex }}>
<Bem elem="Text">Go!</Bem>
</Bem>
);
npm i -S bem-react-core
yarn add bem-react-core
Using loader for webpack.
npm i -D webpack-bem-loader babel-core
webpack.config.js
// ...
module : {
loaders : [
{
test : /\.js$/,
exclude : /node_modules/,
loaders : ['webpack-bem', 'babel']
},
// ...
],
// ...
},
bemLoader : {
techs : ['js', 'css'],
levels : [
`${__dirname}/common.blocks`,
`${__dirname}/desktop.blocks`,
// ...
]
}
// ...
Using plugin for Babel.
npm i -D babel-plugin-bem-import
.babelrc
{
"plugins" : [
["bem-import", {
"levels" : [
"./common.blocks",
"./desktop.blocks"
],
"techs" : ["js", "css"]
}]
]
}
Get sources:
git clone git://github.com/bem/bem-react-core.git cd bem-react-core
Install dependencies:
npm i
Code linting:
npm run lint
Run test:
npm test
Code and documentation copyright 2017 YANDEX LLC. Code released under the Mozilla Public License 2.0.