MUI 5: You Should Be Using SCSS/CSS Modules

Why it’s time to let go of CSS-in-JS

Jeremy Tong
5 min readDec 21, 2021

Introduction

In this first article of the series, I’ll describe why I started using SCSS modules instead of the MUI CSS-in-JS styling engine, and why I think SCSS modules is a better long-term solution.

In the second article, I’ll provide code-snippets on the NextJS + MUI5 + SCSS Modules setup I use, helpful plugins for VSCode, and describe how to overcome restricted access to MUI theme variables in .module.scss files.

Since starting Knit.dev two years ago, I’ve built around 10 different applications using React or Vue with Material Design libraries. I enjoy working with these libraries as they provide incredible UI components out-of-the-box. For most of those projects, I used the library’s recommended styling engines since I wasn’t particularly adept at CSS/SCSS — makeStyles() (MUI 4) and class helpers (Vuetify).

Through working with a startup client with ex-Facebook engineers, I was introduced to SCSS modules as a styling solution in React projects. I immediately noticed the benefits of working with SCSS, especially when provided Figma designs. Since working in that environment, I have developed a strong preference for SCSS modules due to the intuitive syntax, framework interoperability, performance, and workflow.

Syntax

SCSS modules and CSS-in-JS both work by declaring classNames in scoped objects. A CSS-in-JS solution is generally going to look like the following, with classes for a file declared in a flat structure. While you can target nested classes (like assigning a selector ‘& .button’ to a wrapper), doing so would flout the CSS-in-JS styling conventions (it’s not pretty).

The above is defined CSS-in-JS solution is transposed below in a SCSS module file. Notice the indented syntax for nested styles. When a class is applied to a child (button to its wrapper div), styling-to-component consistency is maintained across object representations.

You then import the module into your file, and inject style classes into components.

To my eye, the syntax of CSS/SCSS looks cleaner than the CSS-in-JS equivalent. It’s more consistent with the component, and reduces ambiguity. There is a clear separation between the selectors, properties, and values (and units). SCSS uses hyphen-case as opposed to JS’s camelCase, which is easier to remember since the assigned values follow the same convention.

Framework Interoperability

CSS-in-JS was conceived almost exclusively for React applications. CSS-in-JS solutions are sizable libraries like Emotion, Styled-Components, or JSS. Even the MUI team have waffled on which works best as their internal styling engine, recently switching to Emotion for v5. If you wanted to switch frameworks and maintain styling, you’d have a lot of manual labor ahead of you. Ironically, the more you rely on the “awesome features” of each CSS-in-JS library, the harder it is to dig out.

CSS/SCSS modules can be ported to any common JS framework, Vue, Svelte, or Angular with minimal webpack configuration.

Copy-files, copy-styles

I’m recommending just SCSS here because I think that’s all you need vs. CSS, but you could also add your custom Less, Postcss, and Stylelint config to have a more feature-rich solution.

Performance

CSS-in-JS solutions are technical implementations. You must add a dependency as well as code for runtime handling and <head /> injection. The total bundle size will be larger than the equivalent CSS Module implementation.

Though it isn’t super noticeable, CSS Modules are technically faster.

Workflow

I’ll cover this a bit more in the second article.

Consider this common workflow for programming in React.

  1. Create the component file (styledButton.jsx)
  2. Add barebones JSX that you can assign functionality to test
  3. Add and test component functionality, i.e. state management, hooks, callbacks
  4. Add tentative styles to sub-components in JSX with the style prop and/or other built-in sub-component props
  5. Clean up code by moving styles to a styles object in CSS-in-JS i.e. const style = makeStyles({}). Sometimes, this never happens or doesn’t happen until the entire project is complete

This process with CSS-in-JS can all be done via the component file. I’m also someone who dislikes creating, naming, importing, and organizing new files, so this is a major plus for CSS-in-JS.

However, it’s much harder for someone else to read this code. Often times, developers skimp on step 5, and you end up with styles declared all over a component file.

Since CSS-in-JS styles are scoped to the component file, it is difficult to share styles across components. A common workaround is to declare the same “disabled” style across many components. Code repetition has been heinous in most of my CSS-in-JS projects.

With CSS Modules, you’ll probably need to test tentative styles within the JSX file, but by committing to the styling solution you’ll end up with better programming practices and hygiene. On top of that, CSS modules are much more well-tooled for code editors, so what you might lose in initial convenience by declaring a single file, you make up for with a multitude of VSCode plugins linking the two files and providing CSS autocomplete/snippets.

Dynamic Styling Variables

Dynamic styling variables, or styles that change via the state of the JS component, are a major advantage of CSS-in-JSS. You can apply conditional and theme styles by providing functions that make use of your component variables as with the example below.

With CSS modules, you don’t have the convenience of changing a particular property via a JS variable. Instead, you have to create another selector and style override. If you want to use a theme, it’s even more difficult. You’ll have to inject styles into your SCSS variables declaration, which I’ll explain in my second article. Even with my solution, the theme cannot be altered since it is injected to SASS during Webpack compilation.

Conclusion

To boil down the advantages of CSS-in-JS solutions in one statement, I’d say it’s easier to work with when you don’t already know how to style in a more classical CSS manner.

For all other points mentioned above, CSS modules are the clear winner. It is simply the best modern solution for styling React/Vue/Svelte Javascript projects. It has staying power in that it will never conflict or require accommodations for new Javascript frameworks or the styling libraries that exist for those frameworks.

--

--