How to Style with Next.js and CSS Modules

Introduction

This guide focuses on CSS best practices recommended by Next.js, demonstrates how to implement them with code samples, and also explains why these practices exist. As a bonus, there's a handy solution for using Tailwind with CSS Modules.

Global styles

Next.js prevents importing global CSS in any files other than the following files:

  • _document.tsx
  • _app.tsx
  • /app/layout.tsx

Global files could be:

  • CSS files from frameworks such as Bootstrap
  • Your own library of CSS custom properties
  • Your own utility classes

If you try to import a global css file Next will throw an error:

Global CSS cannot be imported from files other than your Custom <App>. Due to the Global nature of stylesheets, and to avoid conflicts, Please move all first-party global CSS imports to pages/_app.js. Or convert the import to Component-Level CSS (CSS Modules).
Global CSS cannot be imported from files other than your Custom <App>. Due to the Global nature of stylesheets, and to avoid conflicts, Please move all first-party global CSS imports to pages/_app.js. Or convert the import to Component-Level CSS (CSS Modules).

Unless you're importing the CSS from node_modules.

Since Next.js 9.5.4, importing a CSS file from node_modules is permitted anywhere in your application.

Next.js

This encourages good practices and prevents a lot of cascade issues which used to occur before CSS Modules were introduced as the solution. CSS is not as easy as it looks, it doesn't work like JavaScript, so it confuses a lot of developers.

The number of GitHub issues relating to this subject would have driven the core devs up the wall.

If you're interested in the "why" behind this, you can read the RFC that captures the rationale behind the decision.

Local styles

The idea with Next.js development is that you create React components, in each component you can import a specific stylesheet, only for that component, these are local styles.

import s from "./Example.module.css";

const Example = ({ children }) => {
  return <p className={s.root}>{children}</p>;
};

export default Example;
Example TypeScript component, demonstrating how a CSS module is imported into a component

It's the same for SCSS files too.

Adding multiple styles (chaining)

To use multiple classes, I highly recommend using the clsx package, as opposed to rolling your own solution, it's tiny (apparently), and very easy to get used to.

import clsx from 'clsx';
import s from "./Example.module.css";

const Example = ({ children, className, isActive }) => {
  return <p className={clsx(s.root, className, "global-utility", {
    [s.active]: isActive,
  })}>{children}</p>;
};

export default Example;
Demonstrating how to use clsx to apply different classes

You can use boolean logic to apply classes too.

Apply Tailwind styling with CSS Modules

This one isn't recommended as best practice from Tailwind, but sometimes you need to do these things, for whatever reason.

  .root {
    @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
  }
import s from "./Example.module.css";

const Example = ({ children }) => {
  return <p className={s.root}>{children}</p>;
};

export default Example;
Example TypeScript component, demonstrating how a CSS module is imported into a component

Conclusion

We looked at the reasons why global and local styles exist within a Next.js application, and learned how to apply them. We also looked at how to chain CSS classes, and apply classes from multiple sources in a component. Finally, how to use @apply with Tailwind, as you never know when you might need it.

If you have any suggestions as to how the examples can be made clearer, easier to understand, or how I can add any missing use-cases you feel would help others, I'd love nothing more than to hear from you! Reach out to me on Twitter, or shoot me an email.

Written by Morgan Feeney

I’ve been designing and developing for almost 2 decades.
Read about me, and my work.

For juicy tips and exclusive content, subscribe to my FREE newsletter.