TechWizZ

CSS/StylingHow Should I Style My React Application?

React attempts to be as unopinionated as possible. This is both it's greatest strength and weakness.

Over the years, I've tried many different ways to style my React apps. Each solution has its pros and cons. This article will share my journey using CSS with React and how it's evolved to its current state. Then, I'll discuss pros/cons of different approaches for styling.

Vanilla CSS

Like most, I started web development with vanilla CSS. All you need is a single HTML and CSS file. Pretty simple.

<h1 class="header">Welcome</h1>
.header {
  font-size: 32px;
}

As I built more large applications, I began to understand some of the drawbacks.

  • Reusability. It's easy to create append-only stylesheets that become very complex.
  • Global Namespace. Since CSS has a global namespace, you can unintentionally target too many elements.
  • Colocation. It's hard to modularize your CSS, which makes it difficult to delete code safely.

At this point, I was introduced to CSS pre-processors. These tools aim to introduce new funtionality and compile back to vanilla CSS.

Sass

Sass, one of the most popular preprocessors, allows you to write more reusable, maintainable CSS. Some of the most popular features are variables, mixins, and modules.

  • Variables. I want to define global values once and share them throughout my stylesheets.
  • Mixins. I want to reuse particular snippets of CSS.
  • Modules. I want to split up my CSS files for a more maintainable codebase.
$font-stack: Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}
@use "base";

.header {
  font-size: 32px;
}

Sass helped me write more modular, reusable CSS – at least, for a while. Over time, I ran into new issues.

  • Naming. As your CSS grows, it's easy to have class names get out of hand.
  • Browser support. Certain features aren't supported across all browsers and require vendor prefixes like -moz- (Firefox), -webkit- (Safari), and -ms- (Internet Explorer and Edge).

To solve naming, I started to adopt BEM. Block Element Modifier is a methodology that helps you to create reusable components and organize CSS in a modular way, which keeps specificity low.

<button class="button">Cancel</button>
<button class="button button--primary">Submit</button>
.button {
  color: black;
  background-color: gray;
}

.button--primary {
  color: white;
  background-color: green;
}

Naming conventions helped but didn't fix the root cause: naming is hard. For browser support, it's possible to set up your Sass toolchain with a tool like autoprefixer to handle vendor prefixes. However, around this time, I started using component-based frameworks like React. This led me to explore CSS-in-JS.

CSS-in-JS

I started using CSS-in-JS when building a component library. At first, CSS-in-JS solved all my problems.

  • Specificity is solved by auto-generated class names.
  • Colocation is solved by putting CSS directly with the component, making it easy to delete code.
  • Browser support is solved by having autoprefixer built-in.
  • Variables are solved by creating a global theme.

However, naming was still a huge pain.

As the component library grew, I began to explore Theme UI and styled-system. These were more structured approaches to scaling CSS-in-JS to help enforce a design system. Plus, they helped solve naming. Trying to scale styled-components alone in a large application made me get really creative with names. You can only have so many container, wrapper, layout things. For example:

const HomeContainerWrapper = styled.div`
   padding: 8px;
   font-weight: bold;
   color: white;
   background: blue;
`;

<HomeContainerWrapper>
  Hello
</HomeContainerWrapper>

// Forget about naming
<Box
  padding={3}