How I Integrated CSS Modules with SCSS Into My React Application

As a full-stack developer who has worked on many React projects, I‘ve tried a variety of approaches for handling component styles – everything from plain CSS to inline styles to CSS-in-JS libraries. But the setup that has worked the best for me and my teams is the combination of CSS Modules and SCSS.

In this article, I‘ll share why I love this approach and exactly how I integrated CSS Modules with SCSS into my React apps. I‘ll discuss the benefits and drawbacks, show code samples, share configuration tips, and outline some best practices I‘ve developed. By the end, you‘ll have a solid understanding of whether CSS Modules + SCSS is a good fit for your own projects.

Why CSS Modules + SCSS?

First, let‘s talk about the "why". If you‘ve worked on a large React app, you know the pain of trying to maintain a giant global CSS file. It‘s way too easy to introduce class name conflicts, specificity issues, and unnamed magic values.

This is where CSS Modules come in. CSS Modules are a way to write modular, scoped CSS that plays nicely with React‘s component model. The key idea is that each component gets its own CSS file, and all the class names in that file are transformed into unique, globally-safe identifiers when the app is built. This means you never have to worry about class names clashing across components.

Transforming class names from something like this:

/* MyComponent.module.scss */
.heading {
  font-size: 2rem;
  color: blue;
}

Into something like this in the final built app:

<h1 class="MyComponent_heading__abc123">Hello World</h1>

The unique identifier includes the component name, class name, and a content hash to ensure global uniqueness.

Adopting CSS Modules has been a huge win for the maintainability and scalability of the React codebases I work on. It makes it so much easier to reason about styles when you know they are scoped to a specific component. You can change or remove component styles without any fear of breaking unrelated parts of the app.

But to really take full advantage of CSS Modules, I always combine them with a preprocessor like SCSS (Sass). Preprocessors add a ton of useful features on top of vanilla CSS like variables, mixins, math operations, and more. When you‘re writing styles for individual components, these features are really handy for reducing duplication and keeping your styles clean.

Consider a simple example where you want to use your brand color in multiple places. With SCSS variables, you can do:

/* _variables.scss */
$brand-color: #2196f3;
/* MyComponent.module.scss */  
@import ‘./variables‘;

.heading {
  color: $brand-color;
}
/* OtherComponent.module.scss */
@import ‘./variables‘;  

.button {
  background-color: $brand-color;
}

Now if you ever need to change the brand color, you only have to update it in one place instead of doing a global find-and-replace. Multiply this by all the other SCSS features and you can imagine how much more maintainable your styles become.

Setting Up CSS Modules + SCSS

Convinced of the benefits and want to try it yourself? Let‘s walk through the setup.

If you‘re using Create React App 2 (CRA) or newer, CSS Modules are supported out of the box! All you need to do is name your CSS files with the .module.css suffix instead of .css. So MyComponent.css becomes MyComponent.module.css.

To add SCSS support, you‘ll need to install node-sass:

npm install node-sass

Then just use the .module.scss suffix for your files: MyComponent.module.scss. Pretty easy!

If you‘re not using CRA or you ejected from the default config, you‘ll need to configure your build tool to handle CSS Modules. For webpack, that means installing and configuring the css-loader with the modules option set to true:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      // Apply CSS Modules to files ending in .module.scss
      {
        test: /\.module\.scss$/,
        use: [
          ‘style-loader‘,
          {
            loader: ‘css-loader‘,
            options: {
              modules: true,
            },
          },
          ‘sass-loader‘,
        ],
      },
      // Use regular SCSS for all other .scss files  
      {
        test: /\.scss$/,
        exclude: /\.module\.scss$/,  
        use: [‘style-loader‘, ‘css-loader‘, ‘sass-loader‘],
      },
    ],
  },
};

This webpack config will handle both .module.scss files as CSS Modules and regular .scss files as global SCSS. You can put your reusable variables and mixins in a global.scss file and import it into your .module.scss files.

If you‘re using a different build tool like Rollup or Parcel, check their docs for how to enable CSS Modules and SCSS support.

How to Use CSS Modules in Components

With your build pipeline set up, you can start writing modular styles! Here‘s a typical example of how to use CSS Modules in a React component:

import React from ‘react‘;
import styles from ‘./MyComponent.module.scss‘;

const MyComponent = () => (  
  <div className={styles.wrapper}>
    <h1 className={styles.heading}>Hello World</h1>
    <button className={styles.button}>Click Me</button>
  </div>
);

export default MyComponent;

The key parts are:

  1. Import your stylesheet as an object called styles (or whatever name you want)
  2. Use styles.classname as the value for the className prop instead of a plain string

Under the hood, when webpack (or another build tool) processes this code, it will:

  1. Compile the SCSS into plain CSS
  2. Transform the class names into unique identifiers
  3. Return an object mapping the original class names to the transformed ones

So styles.heading will resolve to something like MyComponent_heading__abc123 in the final DOM.

One tip is to use camelCase names in your stylesheets to make them easier to use as JavaScript identifiers. So .page-header becomes .pageHeader and you can use it as styles.pageHeader instead of styles[‘page-header‘].

Integrating with Global Styles

Even when using CSS Modules for the majority of your styling, there will still be cases where you need some global styles. For example:

  • CSS resets and normalizers
  • Typography and other base styles
  • Utility classes used across many components
  • Styling 3rd-party components that don‘t support CSS Modules

For these cases, I create a global.scss file with all my reusable and non-modular styles. Then I import it at the top-level of my app, usually in the main App.js file or in the HTML template if using server-side rendering.

/* global.scss */

/* CSS Reset */
html, body, div, span, h1, h2, h3, p, … {
  margin: 0;
  padding: 0; 
  border: 0;
  vertical-align: baseline;
}

/* Typography */
body {
  font-family: ‘Helvetica Neue‘, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: #333;
}

h1, h2, h3 {
  font-weight: 600;
  margin-bottom: 1rem;  
}

/* Utility Classes */
.p-sm {
  padding: 0.5rem;  
}

.m-lg {
  margin: 2rem;
}
// App.js
import React from ‘react‘;
import ‘./global.scss‘;

const App = () => (
  // ...
);

By keeping your global styles separate, it‘s easier to see which styles are truly reusable versus which are component-specific.

Why Not Use CSS-in-JS?

At this point, you may be wondering why even bother with a separate styling language like SCSS when you could just use a CSS-in-JS library like styled-components or emotion. It‘s a valid question and there are definitely pros and cons to both approaches.

CSS-in-JS libraries allow you to write your styles directly in your component code, usually using tagged template literals. For example, with styled-components:

import styled from ‘styled-components‘;

const Heading = styled.h1`
  font-size: 2rem;
  color: blue;
`;

const MyComponent = () => (
  <Heading>Hello World</Heading>  
);

The main benefits of CSS-in-JS are:

  • No build step required (styles are extracted at runtime)
  • Automatic vendor prefixing
  • Dynamic styles based on component props
  • Scoped styles without extra tooling (CSS Modules)
  • Easier to share values between JS and CSS

Sounds pretty good! So why do I still prefer CSS Modules + SCSS?

Firstly, I find that separating my markup, styles, and behavior makes my components easier to read and maintain. With CSS-in-JS, you often end up with very large component files that mix the three concerns together. In practice, I‘ve found this can make the code harder to scan and understand, especially for developers who are less familiar with the codebase.

CSS Modules + SCSS allows me to keep my styles in separate files while still avoiding global conflicts. I can take advantage of the SCSS ecosystem and tooling, like linters and formatters. And I find the SCSS syntax more expressive and easier to read than tagged template literals.

Performance is another factor. CSS-in-JS libraries extract and inject styles at runtime, which can slow down the initial render of your app. CSS Modules are compiled at build time, so there‘s no runtime overhead. For large apps, this can make a noticeable difference.

All that said, the "right" approach depends on your app‘s specific needs and your team‘s preferences. CSS-in-JS can be a great choice for some projects, especially if you need a lot of dynamic styles. But for most of the apps I‘ve worked on, I‘ve found CSS Modules + SCSS to be a simpler and more maintainable approach.

Real-World Experiences and Benefits

I‘ve used CSS Modules on multiple large React projects and have seen firsthand how they can improve the maintainability and scalability of a codebase. Here are a few specific examples:

  • Avoiding naming conflicts: On a previous project, we had a lot of issues with class name conflicts causing unexpected style bugs. After migrating to CSS Modules, we were able to completely eliminate these issues. Each component‘s styles were scoped to that component, so there was no chance of conflicts.

  • Reducing style duplication: By combining CSS Modules with SCSS features like variables and mixins, we were able to significantly reduce the amount of duplicated styles across our components. Common values like colors, font sizes, and spacing could be defined once and reused everywhere.

  • Simplifying refactors: Because each component‘s styles were isolated, we could more easily refactor or remove components without worrying about breaking styles elsewhere in the app. This gave us a lot more confidence when making changes and helped us move faster.

  • Improving performance: After switching to CSS Modules, we saw a noticeable reduction in the amount of CSS we were shipping to users. Because styles were scoped to specific components, we could more easily identify and remove unused styles. This resulted in smaller bundle sizes and faster load times.

  • Enabling code splitting: CSS Modules work great with React‘s code splitting features. Each dynamically-loaded component can have its own CSS file that is loaded on demand. This helps keep our initial bundle size down and improves performance.

Of course, CSS Modules aren‘t a silver bullet. There are still challenges like managing global styles, dealing with 3rd-party components that don‘t support CSS Modules, and handling complex dynamic styles. But in my experience, the benefits far outweigh the drawbacks for most projects.

Summary and Resources

I hope this deep dive into CSS Modules and SCSS has been helpful! To recap, the main points are:

  • CSS Modules provide a way to write scoped, modular styles that avoid global conflicts
  • SCSS enhances CSS Modules with useful features like variables, mixins, and nesting
  • CSS Modules + SCSS are a powerful combination for styling React components in a maintainable way
  • Setting up CSS Modules in a React project is easy, especially with Create React App
  • CSS Modules can be combined with global styles for maximum flexibility
  • CSS-in-JS is an alternative approach with its own pros and cons

If you want to learn more, here are some additional resources:

Feel free to reach out if you have any other questions! I‘m always happy to discuss React styling approaches in more depth. Happy coding!

Similar Posts