UPDATE: I’ve written a follow up post here about mixin theming which ties in with this approach.

If you’re developing a React app with many components, some of which are reusable, then you can find yourself running into all sorts of problems with CSS when it comes to reusing style, or overiding style in other components and it can get messy and difficult to maintain quite quickly.

I found that this pattern works quite nicely if you want to keep things composable when working with a large application.

Say you have a simple loading spinner component that will be reused in multiple contexts, you might define it like so:-

import { Component } from 'react';
import './LoadingSpinner.scss';

export class LoadingSpinner extends Component {
  render {
    const { rootClass } = this.props;

    return (
      <div className={ `${rootClass}--spinner__container` }>
        <img className={ `${rootClass}--spinner__img` }/>
        <p className={ `${rootClass}--spinner__message` }>Loading ..</p>
      </div>
    )
  }
}

Now you can use SCSS mixins to design a composable style for this. Note the use of SCSS’s ampersand operator, this is actually integral to keping the code dry in this solution because it creates a contract between the mixin and the component that it gets included in, by enforcing consistent selectors.

@mixin spinnerContainer() {
  &__container {
    display: flex;
  }
}

@mixin spinnerImg() {
  &__img {
    padding: 4px;
    flex-direction: column;
  }
}

@mixin spinnerMessage() {
  &__message {
    flex-direction: column;
  }
}

@mixin spinner() {
  &--spinner {
    @include spinnerContainer();
    @include spinnerImg();
    @include spinnerMessage();
  }
}

Now you want to use your component and position it on load, so you include it in a parent

import { Component } from 'react';
import LoadingSpinner from 'spinner';

import './Page.scss';

export default class Page extends Component {
  render {
    const baseClass = 'page';
    return (
      <div className={ baseClass }>
        <h1>Cardboard Box</h1>
        <LoadingSpinner rootClass={ baseClass } />
      </div>
    )
  }
}

Here is a jsfiddle to that effect.

Now you can import your spinner style and have it scoped only to the page selector, with very little source duplication.

@import './LoadingSpinner.scss'

.page {
  .h1 {
    color: red;
  }

  @include spinner();
}

Now imagine you want to include a much smaller version of this component in a different context, that doesn’t have the loading message included, some imaginary updateable item for example.

import { Component } from 'react';
import LoadingSpinner from 'spinner';

import './ListItem.scss';

export default class ListItem extends Component {
  render {
    return (
      const baseClass = 'list-item';
      <li className={ baseClass }>
        <p>Updateable Field</p>
        { loading &&
          <LoadingSpinner noMessage rootClass={ baseClass }/>
        }
      </li>
    )
  }
}

The mixin approach now allows us to code a style based on composition, with no side effects across components.

@import './LoadingSpinner.scss'

.page {
  .h1 {
    color: red;
  }

  &--spinner {
    @include spinnerContainer();
    @include spinnerImg();
  }
}

This will give you selectors in the spinner markup like:

<div class="page--spinner__container">...</div>

<div class="list-item--spinner__container">...</div>

Which keeps the style completely modular with no collision worries. My question to the community is, how do you see this kind of approach affecting compiled CSS size and page/component load/chunking? Also, how does this compare with styled components and other CSS in JS approaches?