Neo Ighodaro Neo Ighodaro is a twenty-something-year-old fullstack web developer and graphic designer not based in Lagos, Nigeria.

Styling in React: 5 ways to style React apps

13 min read 3709

Editor’s note: This styling in React post was last updated on 8 December 2022 to include sections on styling with Sass and CSS style sheet, the pros and cons for each styling method, and a discussion on the best way to style in React.

So, you’re here to learn about styling in React? Well, you’ve come to the right place. When you’re finished reading this post, I guarantee you’ll know to do just that. In this article, we will review styling React components with inline styling, styled-components, CSS modules, Tailwind CSS, and Sass and CSS style sheets.

We will use a component that is part of a to-do application to explain these methods. While also considering the pros and cons of the styling options examined in the article.

If you are new to React, you can check the official documentation.

Jump ahead:

Setting up your React application

To set up an application, you can use Create React App. It is the easiest way to get started with a React project. A CRA demo is out of the scope of the article, though, so we’ll skip that and style a make-believe to-do application.

Here is the demo of the React app:

Let’s get started with the first method!

Styling React components with inline styling

If you are familiar with basic HTML, you’ll know that it is possible to add your CSS inline. This is similar in React.

We can add inline styles to any React component we want to render. These styles are written as attributes and are passed to the element. Let’s style parts of our component using inline styles:

/** src/todo/AddTodo.js **/

//...code omitted for brevity
const AddTodo = () => {
 //...
  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <h2 style={{ padding: "10px 20px", textAlign: "center", color: "white" }}>
        TODO
      </h2>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        }}
      >
        <label
          style={{ padding: "10px 20px", textAlign: "center" }}
          htmlFor="new-todo"
        >
          What needs to be done?
        </label>
      </div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        }}
      >
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </div>
      {message && (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center"
          }}
        >
          <h4 style={{ color: "red" }}>{message}</h4>
        </div>
      )}
    </div>
  );
};
export default AddTodo;

In the code above, we added inline styles to the HTML elements in the AddTodo(). Here are some things you should note about this.

First, there are two curly brackets. What we are rendering is written in JSX, and for pure JavaScript expressions to be used in JSX, they have to be included in curly brackets.

The first curly bracket injects JavaScript into JSX. The inner curly brackets create an object

literal. The styles are passed as object literals to the element.

💡JSX is a preprocessor step that adds XML syntax to JavaScript. You can definitely use React without JSX, but JSX makes React a lot more elegant. Just like XML, JSX tags have a tag name, attributes, and children.

The next thing to note is that the properties are separated by a comma. This is because what we are passing is an object. Because it is a JavaScript attribute, the attributes are written in camelCase and not separated by a dash.

In the code above, we just added a few properties to the elements we styled. However, imagine we had to add more and more styles to the element. This is where the inline method breaks down because it will not look clean.

There is a way around this, though. We can create object variables and pass them to the elements.

Creating a style object variable

We create a style object variable the same way we create a JavaScript object. This object is then passed to the style attribute of the element we want to style.



So, instead of adding the styles inline directly, as we did in the previous example, we just pass the object variables as you see below:

/** src/todo/AddTodo.js **/
const AddTodo = () => {
//...
  return (
    <div style={Container}>
      <h2 style={Header}>TODO</h2>
      <div style={LabelContainer}>
        <label style={Label} htmlFor="new-todo">
          What needs to be done?
        </label>
      </div>
      <div style={FormContainer}>
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </div>
      {message && (
        <div style={Message}>
          <h4 style={{ color: "red" }}>{message}</h4>
        </div>
      )}
    </div>
  );
};

const Container = { display: "flex", flexDirection: "column" };
const Header = { padding: "10px 20px", textAlign: "center", color: "white" };
const LabelContainer = { display: "flex", justifyContent: "center", alignItems: "center"};
const Label = { padding: "10px 20px", textAlign: "center" };
const FormContainer = { display: "flex", justifyContent: "center", alignItems: "center"};
const ErrorMessage = { display: "flex", justifyContent: "center", alignItems: "center"};

In the code above, we created six object variables: Container, Header, LabelContainer , Label, FormContainer, and ErrorMessage.

Then, we pass these variables to the element instead of typing them directly.

💡We did not have to use double curly brackets in the element because these variables are objects themselves.

If you look at the object properties, the camelCases will be converted to dash-separated CSS attributes during compilation. For instance, this:

backgroundColor: "#44014C",
minHeight: "200px",
boxSizing: "border-box"
In plain CSS, these will be written as:
background-color: #44014C;
min-height: 200px;
box-sizing: border-box;

⚠️The camelCase to dash-separated string change applies only to the property names and not property values.

It is possible to pass a variable as a value to a property. So, we can do this:

/* in .js file */
const spacing = "10px 20px";
const Header = {
  margin: spacing,
  padding: spacing
  // ...
}

In many JavaScript environments, creating a global object variable may be bad practice, but it’s fine in React. Because files are not visible to other files unless imported, we can create as many object variables even with the same name without conflict.

Sharing styles across many React components

The style objects and the components do not have to be in the same file. We can create a separate .js file for our styles, export these styles, and then import them into the component where we want to use them. Doing this makes styles reusable across multiple components. Let’s do this for our component.

First, we’ll create a separate .js file called styles.js. Then, we’ll add these styles:

const Container = { display: "flex", flexDirection: "column" };
const Header = { padding: "10px 20px", textAlign: "center", color: "white" };
const LabelContainer = {
  display: "flex",
  justifyContent: "center",
  alignItems: "center"
};
const Label = { padding: "10px 20px", textAlign: "center" };
const FormContainer = {
  display: "flex",
  justifyContent: "center",
  alignItems: "center"
};
const ErrorMessage = {
  display: "flex",
  justifyContent: "center",
  alignItems: "center"
};

export const styles = {
  Container: Container,
  Header: Header,
  LabelContainer: LabelContainer,
  Label: Label,
  ErrorMessage: ErrorMessage,
  FormContainer: FormContainer
}

export const styles = {
  Container: Container,
  Header: Header,
  LabelContainer: LabelContainer,
  Label: Label,
  ErrorMessage: ErrorMessage,
  FormContainer: FormContainer
}

In the code above, we can export each style object individually, which will also mean importing them individually. That might get tedious if there are many style objects in the file.

Therefore, creating an object that contains all styles only makes sense. This object is exported and imported once to the component where it will be used. So, let’s do that:

/** AddTodo.js file **/

// Import the styles
import {styles} from "./styles";


const AddTodo = () => {
//....
  return (
    <div style={styles.Container}>
      <h2 style={styles.Header}>TODO</h2>
      <div style={styles.LabelContainer}>
        <label style={styles.Label} htmlFor="new-todo">
          What needs to be done?
        </label>
      </div>
      <div style={styles.FormContainer}>
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </div>
      {message && (
        <div style={styles.ErrorMessage}>
          <h4 style={{ color: "red" }}>{message}</h4>
        </div>
      )}
    </div>
  );
};

Above the AddTodo(), we’ll import the styles object. This object is then used to style components of our React app and is used just like any JavaScript object.

Pros and cons

The thing to take home from this is that styles can be used and reused in multiple components. The styles just need to be imported and added to the style attribute.

Of course, there are situations where you should not use inline styling, and that’s where our next two methods come in.

Pros:

Using inline styling can help you quickly prototype your interface.

Cons:

The styles declaration can quickly get messy and disorganized with inline styling. Additionally, inline styling does not encourage the DRY (Don’t Repeat Yourself) principle. Using inline styling is not suitable for large projects with a lot of code.

Using styled-components

With styled-components, we can write actual CSS in our JavaScript file. This means you can use all the features of CSS — like media queries, pseudo-selectors, nesting, and more — in JavaScript.

styled-components use ES6’s tagged template literals to style components. With it, the mapping between components and styles is removed. This means that when you’re defining your styles, you’re actually creating a normal React component that has your styles attached to it.

Using styled-components, we can create reusable components with styles. It is quite exciting to create and use.

To get started, we need to install it by running $ npm install --save styled-components in your React app’s directory.

Let’s go back to our to-do app and make our component use styled-components. Firstly, import the styled-components package in your AddTodo.js file with import styled from 'styled-components';.

Now, we can start using it right away. We’ll first create a styled-component, then see how we’ll use it:

/** AddTodo.js file **/
const Container = styled.div`
  display: flex;
  flex-direction: column;
`;
const Header = styled.div`
  padding: 10px 20px;
  text-align: center;
  color: white;
`;
//....

In the code above, we created a component that can be used the same as any React component. However, notice that we are using pure CSS in a JavaScript file. Next, let’s put this component:

/** AddTodo.js file **/

function AddTodo() {
 //...


return (
    <Container>
      <Header>TODO</Header>
      <LabelContainer>
        <Label htmlFor="new-todo">What needs to be done?</Label>
      </LabelContainer>
      <FormContainer>
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </FormContainer>
      {message && (
        <ErrorContainer>
          <ErrorMessage>{message}</ErrorMessage>
        </ErrorContainer>
      )}
    </Container>
  );
}

In the code above, we used the styled-component we created and replaced the HTML tags with the defined component style tags. The styled-component is used as any other HTML element. The only difference is that it comes with its own predefined styles.

You can access the code in the CodeSandbox playground:

Pros and cons

To find out more about styled-components and how to use them, you can read about it here.

Pros:

Styled-components dynamically style your elements in whichever way you deem fit. They encourage the DRY principle with great patterns for organizing your code, and styled-components have compatibility with a wide range of frameworks and libraries. They are also great for developing and maintaining design systems.

Cons:

Styled-components come with a large computing overhead when converting declarations to plain CSS. This can affect the performance of your application. Styled-components also require time to get familiar with the syntax and procedures.

Next, let’s discuss the third way to style in React.

CSS Modules to style React components

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. Take note of the words scoped locally. Let’s break that down a little bit.

CSS class names and animation names are scoped globally by default. This can lead to conflict, especially in large style sheets, because one style can override another. CSS Modules solve this problem. CSS classes are only available within the component where they are used.

A CSS module is basically a .css file that is compiled. When compiled, it produces two outputs. One is CSS, a modified version of input CSS with the renamed class names. The other is a JavaScript object that maps the original CSS name with the renamed name.

Alright, let’s create a CSS class in a module for a sample error message. The name of our module is styles.module.css:

.error-message {
  color: red;
  font-size: 16px;
}

When compiled, this will produce something like this:

.error-message_jhys {
  color: red;
  font-size: 16px;
}

The added jhys is just a sample key (which I added myself) that is used to uniquely identify this class. As said earlier, it produces a JavaScript object, which can be imported into the React file and used:

{
  error-message: error-message_jhys
}

Now, let’s see how we can use:

/** .js file **/
import styles from './styles.css';


function Message() {
 return (
   <div className={styles.ErrorMessage}>I am an error message</div>
 );
}

Pros and cons

Remember, the main purpose of CSS Modules is to make CSS classes locally scoped and avoid conflicts in naming. Here are some pros and cons of using CSS Modules.

Pros:

CSS Modules can easily integrate with CSS or SCSS styling engines. They generate unique class names that are void of conflicts and have inbuilt support in the React library. CSS Modules also fix global scope issues for CSS declarations.

Cons:

Referencing class names with CSS Modules can be confusing most times.

Styling with Tailwind CSS

Tailwind CSS offers a different approach in which no CSS needs to be written to style an application. Instead, Tailwind CSS uses utility classes for each CSS property that you can use directly in your HTML or JSX.

Perhaps you are wondering if each CSS attribute is mapped to a class; what is the bundle size for the final CSS? In fact, the bundle size is very small, with most projects shipping bundle sizes lower than 10kB. But how? Tailwind CSS processes the classes you use during building time and builds a CSS bundle tailored to your project.

Tailwind CSS offers much more than CSS classes mapped to attributes; it’s also a complete framework that supports responsive behavior, grids, states, dark mode, and more. In addition, it’s highly configurable.

To set up Tailwind CSS on a CRA project, a few steps (and libraries) are involved. This is because of what it requires to modify the application’s build process to generate the CSS bundles.

First, begin by installing Tailwind CSS and generating tailwind.config.js and postcss.config.js.

Then, run the $ npm install -D tailwindcss postcss autoprefixer and $ npx tailwindcss init -p commands.

Note, all Tailwind CSS-related libraries are installed as dev packages, meaning that they won’t affect your JavaScript bundle size.

In your tailwind.config.js generated above, add the following value to content array:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}",],
  theme: {
    extend: {},
  },
  plugins: [],
}

Now, we need to set up our CSS baseline. Because Tailwind CSS uses multiple classes with relative values, it is important to set up the same CSS style base across all browsers. Tailwind CSS comes with some default styling to do just that.

To achieve that, navigate to your src/index.css file and paste the code:

/* ./src/styles.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

💡Note, importing these classes will make all your default elements behave differently than what you expect, so elements such as headings and paragraphs will have the same styling properties until you add Tailwind CSS classes to them.

Tailwind CSS offers many configuration options during its set-up, so if you want to learn more, check out the official documentation.

Finally, use Tailwind CSS classes in your project. With your setup ready, you can now use CSS classes directly on your project. Go ahead and build a ToDo component using Tailwind CSS:

/** AddTodo.js file **/

function AddTodo() {
 //...

  return (
    <div className="flex flex-col">
      <h2 className="px-2.5 py-5 text-center text-white">TODO</h2>
      <div className="flex justify-center items-center">
        <label className="px-2.5 py-5 text-center" htmlFor="new-todo">
          What needs to be done?
        </label>
      </div>
      <div className="flex justify-center items-center">
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </div>
      {message && (
        <div className="flex justify-center items-center">
          <h4 className="text-red-800">{message}</h4>
        </div>
      )}
    </div>
  );
}

Notice these classes are directly injected as text to the className prop and that there is a complete reference to all the class names with states and responsive attributes that can be used.

Pros and cons

Though it may seem counterintuitive at first, once you work with Tailwind CSS for a few days, chances are you will love it. Let’s look at some of the pros and cons of Tailwind CSS.

Pros:

Tailwind CSS is easy to use for customizing elements and building clean user interfaces. Tailwind CSS offers a huge reduction in writing custom CSS code, which allows you to develop UI screens faster.

Cons:

Tailwind CSS often requires you to implement it from scratch, as basic components like buttons, navbars, and tabs are not provided. Also, learning Tailwind CSS requires a minimal learning curve.

Sass and CSS style sheets in React

Sass (Syntactically Awesome Style sheet) is a CSS preprocessor with lots of features for creating reusable styles, functionalities for nesting, and organizing CSS declarations. Using Sass or CSS style sheet is based on writing styles in an external file and importing it into the component that needs it.

In summary, Sass is all about writing standard CSS with added benefits. Also, you can write your styles using Sass or SCSS syntax.

This guide uses the SCSS syntax to demonstrate its usage in the to-do application.

To get started, add the node-sass dev dependency to your project with the npm install node-sass command. Also, update your .css file to a .scss file extension.

In the code below, you will see a mixture of CSS and SCSS syntax. The SCSS engine permits writing normal CSS while also leveraging its additional features:

/** todo.scss file **/

// declare global variables
$paddingVertical: 10px;
$paddingHorizontal: 20px;
$text-center: center;
$text-white: #ffffff;
$text-black: #000000;
$text-red: rgb(185 28 28);
$font-size: 16px;
.error {
  color: $text-red;
}
.container{
  display:flex;
  flex-direction: column;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
%typo-large {
  padding: $paddingVertical $paddingHorizontal;
  text-align: $text-center;
}
.h2 {
 @extend %typo-large;
 color: $text-white;
}

.label {
 @extend %typo-large;
}

Next, import the styles declaration into your component. So, in the AddTodo file, import the content of the todo.scss files, then you can access your styles as depicted below:

/** AddTodo.js file**/

//...imports omitted for brevity

// Import the styles
import "./todo.scss";


function AddTodo() {
 //...code omitted for brevity
return (
    <div className="container">
      <h2 className="h2">TODO</h2>
      <div className="flex-center">
        <label className="label" htmlFor="new-todo">
          What needs to be done?
        </label>
      </div>
      <div className="flex-center">
        <form onSubmit={handleSubmit}>
          <input onChange={onChange} value={task} ref={inputRef} />
          <button>Add </button>
        </form>
      </div>
      {message && (
        <div className="flex-center">
          <h4 className="error">{message}</h4>
        </div>
      )}
    </div>
  );
}

Here is the CodeSandbox playground for the above example:

Pros and cons

Let’s consider some of the pros and cons of Sass and CSS style sheets.

Pros:

Sass and CSS style sheets have a wide range of features for dynamically styling user interfaces and have a large community of users and support. Sass and CSS style sheets allow you to integrate with standard CSS code easily.

Cons:

Unfortunately, learning Sass and CSS style sheets comes with a steep learning curve, and Sass and CSS style sheets require some effort in structuring and organizing your SCSS code.

What is the best way to style in React?

While many libraries and CSS engines provide ways for styling in your React application, it is important to note that it’s not a one-size-fits-all approach to settling for the best React styling solution.

In light of this, when looking at the best way to style a React application, several factors vary in different software teams. Here are a few considerations:

  • What are the performance metrics?
  • How easy is it to optimize your code?
  • Do you require a design system?
  • Does the CSS system require extra effort to customize your UI?

These are some of the questions that you will need to answer before concluding on the best way to style in React. Ultimately, the team plays a major role in selecting a CSS solution that best fits a product requirement while considering the expertise and domain knowledge of the team.

Now, it is glaring that there are no straightforward answers to choosing the best library, framework, or system for styling a website built with React. However, this article has outlined five excellent methods for styling in React.

Go on and explore what is suitable for you or your team; you’ll likely settle for any of the CSS solutions discussed in this article.

Conclusion

So, there we have it — five ways of styling in React. Generally, all the methods are useful, and depending on the project’s size; you can use whichever.

If you have any questions or feedback, please leave a comment below.

LogRocket: Full visibility into your production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?

Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.

No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Neo Ighodaro Neo Ighodaro is a twenty-something-year-old fullstack web developer and graphic designer not based in Lagos, Nigeria.

Leave a Reply