DEV Community

Fredrik Bergqvist
Fredrik Bergqvist

Posted on • Edited on • Originally published at bergqvist.it

CSS modules in next.js

On bergqvist.it I used styled jsx for styling my components. I preferred that to other css-in-js frameworks (like JSS) because of it actually using CSS syntax instead of JavaScript objects.

    // styled jsx example with good old CSS
    <style jsx>{`
      .label { color: red; font-style: italic; }
      .article { padding: 0; }
    `}
    </style>

    //JSS example with CSS as a JS object
    const useStyles = createUseStyles({
      label: {
        color: 'red'
        fontStyle: 'italic'
      },
      article: {
        padding: 0
      }
    })
Enter fullscreen mode Exit fullscreen mode

I like Styled jsx but it has had an issue with FOUC in the last few versions of Next.js though, and with Next 12 I decided to try something new and migrate to CSS Modules instead.

What is CSS Modules?

CSS Module is a CSS file in which all class names and animation names are scoped locally by default. 

So the benefit are the same as css-in-js-frameworks but pure css (or in my case scss) files are used instead of keeping the styling in the javascript files.

Read more about CSS Modules here

Why CSS Modules?

I may be old school, but I actually prefer to keep the CSS and JavaScript separated for each other. I can see the benefits of keeping them tightly coupled and can agree that simple, self-contained components probably benefit from this approach, but it gets messy when the component has many styles with media queries.

I also want to use SASS, which is fully supported with CSS Modules in Next.js.

Migrating from styled jsx to CSS Modules

Since Styled jsx use regular CSS it’s actually just a matter of creating the <component>.module.scss-file, importing it into the component and changing the classes

    //styled jsx
    export default function MyComponent(){
      return (<div className={"article"}>
        <span className={"label"}>...</span>
        <style jsx>{`
          .label { color: red; font-style: italic; }
          .article { padding: 0; }
        `}</style>
      </div>)
    }

    //CSS Modules
    import styles from "./MyComponent.module.scss";

    export default function MyComponent(){
      return (<div className={styles.article}>
        <span className={styles.label}>...</span>
      </div>)
    }
Enter fullscreen mode Exit fullscreen mode

Using multiple modules in one component

For reusability you might want to use a css module in more than one component

    import styles from "./MyComponent.module.scss";
    import * as secondaryStyles from "./secondary.module.scss";

    export default function MyComponent(){
      return (<div className={styles.article}>
        <span className={secondaryStyles.label}>...</span>
      </div>)
    }
Enter fullscreen mode Exit fullscreen mode

If you are using TypeScript this approach probably cause an error: TS2339: Property 'label' does not exist on type 'typeof import("*.module.scss")'.

The error can be can be mitigated by adding a typings.d.ts -file to the root of your project with the following content

    // typings.d.ts
    declare module "*.module.scss" {
      interface IClassNames {
        [className: string]: string;
      }
      const classNames: IClassNames;
      export = classNames;
    }
Enter fullscreen mode Exit fullscreen mode

Composition

Instead of importing several different modules it’s possible to compose new classes from existing classes.

    // secondary.module.scss
    .label { 
      color: red; 
      font-style: italic; 
    }

    // MyComponent.module.scss
    .article { 
      padding: 0; 
    }
    .label {
      composes: label from "./secondary.module.scss";
    }

    // MyComponent.tsx
    import styles from "./MyComponent.module.scss";

    export default function MyComponent(){
      return (<div className={styles.article}>
        <span className={styles.label}>...</span>
      </div>)
    }
Enter fullscreen mode Exit fullscreen mode

Global styles

As I already had a global css-file that I imported into my _app.tsx, I really did not have to do anything to get my global classes working.

If you want to add a global class in a component file you can add it by using :global() on the class.

    :global(.label) { 
      color: red; 
      font-style: italic; 
    }
Enter fullscreen mode Exit fullscreen mode

Parting words

I’m quite happy with CSS Modules, the site no longer gets FOUC and it looks great with JavaScript disabled as well!

Hope this could be of any help to someone looking into CSS Modules.

Top comments (0)