Do you need CSS-in-JS with React?

By Dave Ceddia

One of the confusions I see people have with React is the whole situation around CSS. Namely, whether you need to use CSS-in-JS libraries, or whether you need some kind of “React-compatible” styling solution. It shows up in comments like this:

I tried React, but I really didn’t like styled-components.

The underlying assumption being that React and styled-components are inseperable – that choosing to use React means also being locked in to use a CSS-in-JS library.

And it’s not true! You can style a React app with plain CSS, in plain .css files, just the same as you can style HTML (or Vue or Svelte or Angular) with plain CSS.

Here’s some CSS:

button.css
.call-to-action {
  background: red;
  color: #fff;
}

And here’s a React component for a button that uses it:

button.js
import React from 'react';
import './button.css';

const Button = () => {
  return (
    <button className="call-to-action">
      Click Me!
    </button>
  )
}

Notice how we’re importing the CSS file on line 2, and then we can just use the CSS class on the button via the usual className prop.

The import might look weird – importing CSS into JavaScript? – but this is a Webpack* thing. This is the way to tell Webpack “this JS file depends on this other CSS file, please include it on the page”.

*Not just a Webpack thing; other bundlers like Rollup and Parcel work the same.

Apply a style to a React component on hover

CSS is very good at dealing with :hover states, and all of that still works in a React app.

Let’s say we want to change the color of the button when it’s hovered. Here’s the change we’ll make to the CSS:

button.css
.call-to-action {
  background: red;
  color: #fff;
}

.call-to-action:hover {
  background: blue;
}

And that’s it! No changes to the React component are needed.

React doesn’t do styling

Now you might be thinking, “Gee, those React people thought of everything! They even built in support for all those CSS features!”

They didn’t, though :)

React is only a fancy layer on top of HTML. React components output HTML, and CSS styles the HTML.

As far as the browser is concerned, React doesn’t figure into it. The browser applies CSS rules to the HTML on the page, whether that HTML came from an .html file or was generated by React.

Applying an “active” style to a React component

Oh ho, but what about when your style depends on some bit of component state, or a prop? Surely, CSS-in-JS is required then?

Not quite!

You can make the className prop dynamic, using a template string to choose classes on the fly, like this:

button.js
import React from 'react';
import './button.css';

const Button = ({ isActive }) => {
  return (
    <button className={`call-to-action ${isActive ? 'active' : ''}`}>
      Click Me!
    </button>
  )
}

Then you can add the .active class to your CSS file, and you’re done.

button.css
.call-to-action {
  background: red;
  color: #fff;
}

.call-to-action:hover {
  background: blue;
}

.call-to-action.active {
  border: 2px solid orange;
}

Now the button will get the active class and an orange border when the isActive prop is truthy. (the same technique works if isActive is a state variable instead of a prop)

Maybe that looks a bit… ugly, though? No matter! Move it out to its own line.

button.js
import React from 'react';
import './button.css';

const Button = ({ isActive }) => {
  const classes = `call-to-action ${isActive ? 'active' : ''}`;

  return (
    <button className={classes}>
      Click Me!
    </button>
  )
}

Or install the classnames library to build up these strings if you find yourself doing this a lot.

button.js
import React from 'react';
import classnames from 'classnames';
import './button.css';

const Button = ({ isActive }) => {
  const classes = classnames(
    'call-to-action',
    { active: isActive }
  )

  return (
    <button className={classes}>
      Click Me!
    </button>
  )
}

Use the style prop for truly dynamic styling

Maybe you’re building some kind of component that needs to set its CSS style dynamically, based on a JS variable. Maybe a prop, or a bit of state. Surely that requires CSS-in-JS?

Nope.

Here’s a progress bar with a dynamic width, for example. The style prop lets you set CSS properties directly, same as the style attribute on an HTML tag.

progress-bar.js
import React from 'react';
import './progress-bar.css';

const ProgressBar = ({ progress }) => {
  return (
    <div className="progress-bar">
      <div style={{
        width: `${progress}%`;
      }}>
        &nbsp;
      </div>
    </div>
  )
}

The .progress-bar takes up the full width of its container, and then the div inside takes up a proportional width based on the progress. Coloring and other style can be done with regular CSS, but the width has to be dynamic, so we can apply it using the style prop.

The style prop supports camelCased names like borderWidth as properties. The React docs have more to say on the style prop.

progress-bar.css
.progress-bar {
  width: 100%;
  height: 20px;
  border-radius: 2px;
}
.progress-bar > div {
  background: blue;
}

What’s the best way to use CSS in React?

The entire goal of this article was to make it clear that you are not required to use CSS-in-JS with React, that React works with plain CSS just fine.

But should you use a CSS-in-JS library in your projects?

The actual answer is, it’s entirely up to you. If you like CSS-in-JS, go for it. If you haven’t tried it yet, give it a try and see what you think. There are advantages and disadvantages with every approach.

For my own projects, I tend to use either plain CSS or, lately, a utility-first approach with Tailwind CSS. Tailwind, by the way, generates plain CSS, which means Tailwind works great with React and any other UI framework that can use CSS (which, afaik, is all of them).

React Native is a different story, because CSS is not available on native platforms, so you actually do need to use a CSS-in-JS solution there.

But for regular React? Use whatever you want :)