Styling your React app

In this article series, I will highlight and explain common different styling solutions for you React based front end application. The first part will cover inline styles, CSS and pre-processors and CSS Modules. The second part will cover CSS-in-JS and, the last part will cover existing popular component libraries.

Introduction

When deciding on a styling solution for your React application there are a lot of parameters to consider, we might feel insecure about other peoples work, will the solution match your requirements, will it reach the quality goals you have, do you want to build your own component library with the extra time it will take or using existing one to save time, etc...

The fastest and maybe the easiest way is to chose one existing component library and there are a lot of them, however, this is a topic for another article.

How to style components?

When building with React, I personally started with inline styling, went further with CSS Modules and learned Sass and then I ended up using CSS-in-JS bases solutions.

Inline styles

React lets you add CSS inline, written as attributes and passed to elements. In React, inline styles are not specified as a string. Instead they are specified with an object whose key is the camelCased version of the style name, and whose value is the style’s value, usually a string. The style attribute accepts a JavaScript object with camelCased properties rather than a CSS string. This is consistent with the DOM style JavaScript property, is more efficient, and prevents XSS security holes. For example:

/* components/header.js */

function Header() {
  // Style is specified as an object to the style attribute
  return (<h1 className='heading' style={{color: 'red'}}>Heading</h1>)
}

// We can also define the style in a JavaScript 
// object outside the component
const styles = {
  color: 'red'  
}

function Header() {
  return (<h1 className="heading" style={styles.color}>Hello World!</h1>)
}

But, lets say that we are using the Header component in a lot of places, the rendered HTML can end up like this;

<h1 class="heading" style="color: red;">Heading</h1>
<h1 class="heading" style="color: red;">Heading</h1>
<h1 class="heading" style="color: red;">Heading</h1>
<h1 class="heading" style="color: red;">Heading</h1>

Notice how the style attribute is repeated over and over and what happens if someone wants this color red changed the developer in that spot would think CSS first, not in the code.

Inline styles are usually considered bad practice to write in HTML, since it increases the size of the file and makes it harder to maintain, etc.

CSS or pre-processors such as Sass

We can of course use CSS files as we always have and then use the className on our elements to attach our styles.

/* components/header.css */
.heading {
  color: "red";
}
/* components/header.js */
// We are importing our stylesheet
import './styles.css'

function Header() {
  // The heading css class is attach to the component
  return (<h1 className='heading'>Heading</h1>)
}

// Renders this
// <h1 class="heading">Heading</h1>

Note that your styles will not be localized to your component, it will be attached to your DOM elements which means globals problem.

If you want to use CSS I recommend that you are using Sass to create you CSS file(s)

Why should you use Sass over plain CSS?

One of the great benefits of using a CSS pre-processor like Sass is the ability to use variables. A variable allows you to store a value, or a set of values, and to reuse these variables throughout your Sass files as many times you want and wherever you want.

Another fantastic benefit of CSS pre-processors is their improved syntax. Sass allows you to use a nested syntax, which is code contained within another piece of code that performs a wider function.

Using variables is great but what if you have blocks of code repeating in your style sheet more than once? That is when mixins come into play. Mixins are like functions in other programming languages. They return a value or set of values and can take parameters including default values. Note that Sass also has functions, so do not confuse a mixin with a function.

Another advantage of using SASS is the huge amount of documentation available online. From tutorials to books, SASS has all you need to become an expert. The fact is, Sass is all around. Along with Less, one of the other solid alternatives, Sass has become the most used CSS pre-processor in the front-end universe. Some of the best front-end frameworks like Bootstrap and Foundation have been developed with SASS which means you can easily extends them.

CSS Module

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. With CSS Modules, your CSS class names become similar to local variables in JavaScript.

In CSS Modules, each file is compiled separately, so you can use simple class selectors with generic names — you don’t need to worry about polluting the global scope.

Before CSS Modules We might code this up using Suit/BEM-style classnames & plain old CSS & HTML like so:

/* components/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */}
<button class="Button Button--in-progress">Processing...</button>

It’s quite good. We have these four variants but BEM-style naming means we don’t have nested selectors. We’re starting Button with a capital letter so, as to (hopefully) avoid clashes with any of our previous styles or any dependencies we’re pulling in and, we’re adopting the --modifier syntax to be clear that the variants require the base class to be applied.

With CSS Modules CSS Modules means you never need to worry about your names being too generic, just use whatever makes the most sense:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */}

Notice that we don’t use the word “button” anywhere. Why would we? The file is already called “submit-button.css”. In any other language, you don’t have to prefix all your local variables with the name of the file you’re working on — CSS should be no different.

That’s made possible by the way CSS Modules is compiled — by using require or import to load the file from JavaScript:

/* components/submit-button.js */
import { Component } from 'react'
// Load the css file
import styles from './submit-button.css'

export default class SubmitButton extends Component {
  render() {
    let className, text = "Submit"
    if (this.props.store.submissionInProgress) {
      className = styles.inProgress
      text = "Processing..."
    } else if (this.props.store.errorOccurred) {
      className = styles.error
    } else if (!this.props.form.valid) {
      className = styles.disabled
    } else {
      className = styles.normal
    }
    return <button className={className}>{text}</button>
  }
}
// Renders something like this
// <button class="components_submit_button__normal__abc5436">//    Processing...
// </button>

The actual class names are automatically generated and guaranteed to be unique, see line 23.

CSS Modules are one of the most popular ways for styling React components. Whether you are using only CSS or a more advanced pre-processor like SASS, it doesn't matter for CSS Modules: You can write all these styles in your style sheet files next to your React components.

Note: If you are using create-react-app, it comes with CSS Modules out of the box.

If you are not using create-react-app and instead using you own React + Webpack application, you need some set up before you can start using CSS Modules in React.

First, we need some more loaders for Webpack. These loaders enable Webpack to bundle CSS files:

npm install css-loader style-loader --save-dev

Then you need to configure Webpack and in your webpack.config.js file, add the new loaders for interpreting CSS files:

/* webpack.config.js */
module.exports = {
  module: {
    rules: [
      ...
      {
        test: /\.css$/i,
        exclude: /node_modules/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
    ],
  },
  ...
};

The next article in this series will cover CSS-in-JS and hopefully shed some light over how it allows us to use JavaScript to describe styles in a declarative, conflict-free and reusable way.


Published: 2019-11-20
Author: Henrik Grönvall
Henrik Grönvall
Copyright © 2022 Henrik Grönvall Consulting AB