DEV Community

Cover image for Styling React App
Pranay Jain
Pranay Jain

Posted on

Styling React App

When it comes to styling your React app, you have a lot of options. Which do you think you'll choose?

I've broken out the five main approaches you can take when writing CSS in your React project.

For any project, there is no one-size-fits-all option for writing styles in React. Each project is unique, with its own set of requirements.

As a result, at the end of each segment, I'll go over the pros and cons of each approach to help you decide which is ideal for your tasks.

Let's get going.

What We Will Be Coding?

We'll use the same example to see how the code for each of these styling approaches compares to one another: a simple but tidy testimonial card.

Image description

Inline Styles

The simplest approach to style any React application is to use inline styles.

You don't need to create a separate stylesheet if you style items inline.

In comparison to styles in a stylesheet, styles applied directly to elements have a greater priority. This implies that they "override" any other style rules that an element may have.

Here's how we designed our testimonial card with inline styles:

// App.js
export default function App() {
  return (
    <section
      style={{
        fontFamily: "sans-serif",
        fontSize: "1rem",
        fontWeight: 1.5,
        lineHeight: 1.5,
        color: "#4a4d4e",
        backgroundColor: "#fff",
        padding: "0 2em",
        minHeight: "100vh",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <div
        style={{
          textAlign: "center",
          maxWidth: "950px",
          margin: "0 auto",
          border: "1px solid #e6e6e6",
          padding: "40px 25px",
          marginTop: "50px",
        }}
      >
        <img
          src="https://randomuser.me/api/portraits/women/90.jpg"
          alt="Jane Doe"
          style={{
            margin: "-90px auto 30px",
            width: "100px",
            borderRadius: "50%",
            objectFit: "cover",
            marginBottom: "0",
          }}
        />
        <div>
          <p
            style={{
              lineHeight: 1.5,
              fontWeight: 300,
              marginBottom: "25px",
              fontSize: "1.375rem",
            }}
          >
            The simple and intuitive design makes it easy for me use. I highly
            recommend Fetch to my peers.
          </p>
        </div>
        <p
          style={{
            marginBottom: "0",
            fontWeight: 600,
            fontSize: "1rem",
          }}
        >
          Jane Doe
          <span style={{ fontWeight: 400 }}> · Front End Developer</span>
        </p>
      </div>
    </section>
  )
}
Enter fullscreen mode Exit fullscreen mode

Despite a few short-term advantages, inline styles are only appropriate for relatively tiny applications. As your code base increases, the challenges of inline styles become obvious.

Even a simple component like this gets rather hefty if all the styles are inline, as the code sample above illustrates.

However, you may save time by converting inline styles into reusable variables that can be saved in an object:

// App.js
const styles = {
  section: {
    fontFamily: "sans-serif",
    fontSize: "1rem",
    fontWeight: 1.5,
    lineHeight: 1.5,
    color: "#4a4d4e",
    backgroundColor: "#fff",
    padding: "0 2em"
  },
  wrapper: {
    textAlign: "center",
    maxWidth: "950px",
    margin: "0 auto",
    border: "1px solid #e6e6e6",
    padding: "40px 25px",
    marginTop: "50px"
  },
  avatar: {
    margin: "-90px auto 30px",
    width: "100px",
    borderRadius: "50%",
    objectFit: "cover",
    marginBottom: "0"
  },
  quote: {
    lineHeight: 1.5,
    fontWeight: 300,
    marginBottom: "25px",
    fontSize: "1.375rem"
  },
  name: {
    marginBottom: "0",
    fontWeight: 600,
    fontSize: "1rem"
  },
  position: { fontWeight: 400 }
};

export default function App() {
  return (
    <section style={styles.section}>
      <div style={styles.wrapper}>
        <img
          src="https://randomuser.me/api/portraits/women/90.jpg"
          alt="Jane Doe"
          style={styles.avatar}
        />
        <div>
          <p style={styles.quote}>
            The simple and intuitive design makes it easy for me use. I highly
            recommend Fetch to my peers.

          </p>
        </div>
        <p style={styles.name}>
          Jane Doe
          <span style={styles.position}> · Front End Developer</span>
        </p>
      </div>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Despite this improvement, inline styles still miss a number of critical features that a standard CSS stylesheet may provide.

You can't write animations, styles for nested elements (i.e. all child elements, first-child, last-child), pseudo-classes (i.e.:hover), or pseudo-elements, to mention a few things (::first-line).

Inline styles are great for prototyping. However, as the project progresses, you'll need to switch to a different CSS styling option in order to obtain basic CSS capabilities.

Pros:

  • The quickest way to compose styles

  • Prototyping friendly (write inline styles then move to stylesheet)

  • Have a higher priority (can override styles from a stylesheet)

Cons:

  • JSX is unreadable due to a large number of inline styles.

  • Basic CSS capabilities such as animations, selectors, and so on are not available.

  • Doesn't scale very well

Plain CSS

Instead of using inline styles to style the elements of a component, it's more common to utilize a CSS stylesheet.

Writing CSS in a stylesheet is the most common and straightforward method for styling a React application, but it shouldn't be disregarded.

Writing styles in "basic" CSS stylesheets is improving all the time, thanks to the expanding amount of possibilities offered in the CSS standard.

This includes new pseudo-classes like:is and:where, as well as CSS variables for storing dynamic data and complex selectors for precisely picking child items.

Here's how we designed our testimonial card with plain css:

/* styles.css */
body {
  font-family: sans-serif;
  margin: 0;
  font-size: 1rem;
  font-weight: 1.5;
  line-height: 1.5;
  color: #4a4d4e;
  background-color: #fff;
}
.testimonial {
  margin: 0 auto;
  padding: 0 2em;
}
.testimonial-wrapper {
  text-align: center;
  max-width: 950px;
  margin: 0 auto;
  border: 1px solid #e6e6e6;
  padding: 40px 25px;
  margin-top: 50px;
}
.testimonial-quote p {
  line-height: 1.5;
  font-weight: 300;
  margin-bottom: 25px;
  font-size: 1.375rem;
}
.testimonial-avatar {
  margin: -90px auto 30px;
  width: 100px;
  border-radius: 50%;
  object-fit: cover;
  margin-bottom: 0;
}
.testimonial-name {
  margin-bottom: 0;
  font-weight: 600;
  font-size: 1rem;
}
.testimonial-name span {
  font-weight: 400;
}
Enter fullscreen mode Exit fullscreen mode
// App.js
import "./styles.css";

export default function App() {
  return (
    <section className="testimonial">
      <div className="testimonial-wrapper">
        <img
          className="testimonial-avatar"
          src="https://randomuser.me/api/portraits/women/90.jpg"
          alt="Jane Doe"
        />
        <div className="testimonial-quote">
          <p>
            The simple and intuitive design makes it easy for me use. I highly
            recommend Fetch to my peers.
          </p>
        </div>
        <p className="testimonial-name">
          Jane Doe<span> · Front End Developer</span>
        </p>
      </div>
    </section>
  );
}

Enter fullscreen mode Exit fullscreen mode

For our testimonial card, note that we are creating classes to be applied to each individual element. These classes all start with the same name testimonial-.

CSS written in a stylesheet is a great first choice for your application. Unlike inline styles, it can style your application in virtually any way you need.

One minor problem might be your naming convention. Once you have a very well-developed application, it becomes harder to think of unique classnames for your elements, especially when you have 5 divs wrapped inside each other.

If you don't have a naming convention you are confident with (i.e. BEM), it can be easy to make mistakes, plus create multiple classes with the same name, which leads to conflicts.

Traditional CSS is also longer and more repetitive than SASS/SCSS and other newer CSS approaches. As a result, writing CSS styles takes longer than utilizing a library like CSS-in-JS or a tool like SCSS.

Because CSS applies to all children components, a CSS stylesheet applied to a component isn't limited to that component. All of the established rules will be passed down to any elements that are children of your styled component.

If you're comfortable with CSS, it's a feasible alternative for styling any React application.

With that said, there are a variety of CSS libraries available that provide all of the power of CSS with less code and many more features that CSS will never provide on its own (such as scoped styles and automatic vendor prefixing).

Pros:

  • Provides us with all of the current CSS tools (variables, advanced selectors, new pseudoclasses, etc.)

  • It assists us in cleaning up our component files by removing inline styles.

Cons:

  • Vendor prefixing must be set up to ensure that all users have access to the most recent features.
  • It is not scoped, therefore any stylesheet will be applied to the component and its children.

  • You'll need to use a consistent naming convention to avoid clashes in styles.

SASS / SCSS

What is SASS, exactly? SASS is an acronym for "Syntactically Awesome Style Sheets."

SASS offers a variety of useful capabilities not found in ordinary CSS stylesheets. Variables, styles that can be extended, and nesting are all present.

Image description

Despite having a similar syntax to ordinary CSS, SCSS styles do not require the use of open and closing brackets when creating style rules.

As an example, here's a SCSS stylesheet with some nested styles:

/* styles.scss */
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

Enter fullscreen mode Exit fullscreen mode

Compare this with the same code written in a SASS stylesheet:

/* styles.sass */
nav
  ul
    margin: 0
    padding: 0
    list-style: none

  li
    display: inline-block

  a
    display: block
    padding: 6px 12px
    text-decoration: none
Enter fullscreen mode Exit fullscreen mode

Because this isn't standard CSS, it must be converted from SASS to normal CSS. You can use a library like node-sass to accomplish this in our React apps.

Install node-sass with npm to get started with.scss and.sass files:

npm install node-sass
Enter fullscreen mode Exit fullscreen mode

Here's how we designed our testimonial card with scss:

/* styles.scss */
$text-color: #4a4d4e;

%font-basic {
  font-size: 1rem;
}

body {
  @extend %font-basic;
  font-family: $font-stack;
  color: $text-color;
  margin: 0;
  font-weight: 1.5;
  line-height: 1.5;
  background-color: #fff;
}

/* unchanged rules skipped */

.testimonial-name {
  @extend %font-basic;
  margin-bottom: 0;
  font-weight: 600;

  span {
    font-weight: 400;
  }
}
Enter fullscreen mode Exit fullscreen mode

These styles give us the following features: variables, extending styles and nested styles.

Variables: You can use dynamic values by declaring variables with a $ at the beginning, much like in JavaScript.

$text-color can be used in multiple rules.

Extending / Inheritance: Style rules can be extended . You can extend rules by writing your own selector, which you can reuse as a variable. The names of the rules you want to extend begin with the %.

The rules body and .testimonial-name inherit the %font-basic variable.

Nesting: You can stack numerous rules that start with the same selector instead of writing them all separately.

To target the span element within .testimonial-name, we use a nested selector.

You can find a working version of a React application with SCSS here.

Pros:

  • Many dynamic CSS features are present, such as extending, nesting, and mixins.

  • CSS styles can be created with a lot less boilerplate than standard CSS.

Cons:

  • Much like CSS, styles are global and not specific to any one component.
  • CSS stylesheets are starting to include functionality that were previously exclusively available in SASS, such as CSS variables (not necessarily a con, but worth noting)

  • Configuration is frequently required with SASS / SCSS, such as the installation of the Node library node-sass.

CSS Modules

Another minor alternative to CSS or SASS is CSS modules.

CSS modules are useful since they can be used with either regular CSS or SASS. Additionally, if you're using Create React App, you may start using CSS modules right away.

Here's an example of a CSS module-based application:

/* styles.module.css */
body {
  font-family: sans-serif;
  margin: 0;
  font-size: 1rem;
  font-weight: 1.5;
  line-height: 1.5;
  color: #4a4d4e;
  background-color: #fff;
}

/* styles skipped */

.testimonial-name span {
  font-weight: 400;
}

Enter fullscreen mode Exit fullscreen mode
// App.js
import styles from './styles.module.css';

export default function App() {
  return (
    <section className={styles.testimonial}>
      <div className={styles['testimonial-wrapper']}>
        <img
          src="https://randomuser.me/api/portraits/women/90.jpg"
          alt="Jane Doe"
          className={styles['testimonial-avatar']}
        />
        <div>
          <p className={styles['testimonial-quote']}>
            The simple and intuitive design makes it easy for me use. I highly
            recommend Fetch to my peers.
          </p>
        </div>
        <p className={styles['testimonial-name']}>
          Jane Doe
          <span> · Front End Developer</span>
        </p>
      </div>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Before the extension .css, our CSS file has the name .module. Any CSS module file must begin with the word "module" and conclude with the proper extension (CSS or SASS/SCSS).

When we look at the code above, we can see that CSS modules are written exactly like regular CSS, but they are imported and utilized as if they were generated as objects (inline styles).

The advantage of CSS modules is that they assist us avoid the problem of class clashes that we have with standard CSS. When our project is constructed, the attributes we're referencing become unique classnames that can't conflict with one another.

Our HTML elements will appear as follows:

<p class="_styles__testimonial-name_309571057">
  Jane Doe
</p>
Enter fullscreen mode Exit fullscreen mode

The issue of global scope in CSS is also addressed with CSS modules. Unlike traditional CSS stylesheets, CSS declared using modules to individual components will not cascade to child components.

CSS modules are recommended over CSS and SASS for avoiding class clashes and designing predictable styles that only apply to one or another component.

Pro:

  • Unlike CSS / SASS, styles are limited to one or more components.

  • Classnames picked at random ensure that there is no collision of styles.

  • They are compatible with SASS and CSS.

Cons:

  • Classnames can be tough to remember.

  • When employing CSS styles, such as object properties, there may be a learning curve.

CSS-in-JS

CSS-in-JS enables us to create CSS styles directly in the (.js) files of our components.

It not only allows you to write CSS style rules without having to create a .css file, but it also allows you to scope these styles to particular components.

In other words, you can add, update, or remove CSS without fear of being caught off guard. Changing the styles of one component has no effect on the styles of the rest of your application.

CSS-in-JS frequently employs a form of JavaScript function known as a tagged template literal. The best part is that we can still write normal CSS style rules directly in our JS!

Here's a quick example of a popular CSS-in-JS package, Styled Components:

// App.js
import styled from "styled-components";

const Button = styled.button`
  color: #ff6700;
  border: 2px solid #ff6700;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  &:hover {
    opacity: 0.9;
  }
`;

export default function App() {
  return (
    <div>
      <Button>Click me</Button>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Result:

Image description

Take note of the following:

  1. You can create standard CSS styles as well as nested styles and pseudo-classes (like hover).

  2. Styles can be applied to any valid HTML element, such as the button element mentioned above (see styled.button).

  3. You can use these styles to create new components, as shown in component Button.

Can props be sent to this component?  Yes! We can export this component and use it everywhere we want in our app, as well as add dynamic functionality to it with props.

Let's imagine you want an inverted version of Button, with the backdrop and text inverted. It's no problem.

Pass the inverted prop to our second Button, and you can use the ${} syntax with an inner function in Button to retrieve all props supplied to the component.

// App.js
import styled from "styled-components";

const Button = styled.button`
  background: ${(props) => (props.inverted ? "#ff6700" : "#fff")};
  color: ${(props) => (props.inverted ? "#fff" : "#ff6700")};
  border: 2px solid #ff6700;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  &:hover {
    opacity: 0.9;
  }
`;

export default function App() {
  return (
    <div>
      <Button>Click me</Button>
      <Button inverted>Click me</Button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can select the inverted prop and use a ternary operator to conditionally determine the color of the background and text of button.

Here's the result:

Image description

There are a slew of other advantages to using a CSS-in-JS library to style your React applications (too many to name here), but I'll go through a few of them below.

Check out Emotion and Styled Components, two of the most popular CSS-in-JS libraries for React.

Using CSS-in-JS libraries comes with the drawback of introducing another library to your project. However, I would say that the increased developer experience you get from styling your React apps over basic CSS is well worth it.

Pros:

  • Styles are scoped to specific components.

  • We can now export, reuse, and even extend our styles using props because our CSS is now JS.

  • CSS-in-JS libraries generate unique classnames for your written styles, ensuring that there are no styling conflicts.

  • You don't need to worry about class naming conventions; simply create styles!

Cons:

  • Unlike basic CSS, you'll need to install one or more third-party JavaScript libraries, which will make your finished project heavier.

Conclusion

It's worth noting that in this comparison, I didn't include component libraries. I wanted to focus on a few ways for creating your own styles.

Remember that for your project, adopting a library with pre-made components and styles, such as Material UI or Ant Design (to name a few), is a completely valid option.

After reading this article, I hope you have a good understanding of how to style your React apps and which method to apply for your next project.

Top comments (0)