Hello Sunil
css-modules-the-ultimate-guide-featured-image

CSS Modules: The Ultimate Guide to Scalable and Modular Styling

CSS Modules have emerged as a powerful tool for managing styles in component-based framework like Next.js. By scoping styles locally to components, they solve common problems like global namespace conflicts and style leakage, making them a favorite for developers building scalable applications.

In this blog, we’ll explore what CSS Modules are, why they’re effective, and how to use them efficiently.

CSS Modules are CSS files where all class names and animation names are scoped locally by default. When you import a CSS Module into your JavaScript or TypeScript file, the class names are automatically transformed into unique identifiers.

  • Encapsulation: Styles are tied to components, ensuring no unintentional impact on other parts of the app.
  • Scalability: CSS Modules work well in large projects with many developers.
  • Flexibility: Compatible with CSS preprocessors like Sass for advanced features.
  • Easy Adoption: CSS Modules integrate seamlessly with tools like Webpack and frameworks like Next.js.

1. Setting Up CSS Modules in Next.js

Next.js supports CSS Modules out of the box. Create a .module.css file, and you’re ready to go.

1. Example Directory Structure:

components/
├── Button/
│   ├── Button.module.css
│   ├── Button.js

2. Writing Styles

In the Button.module.css file:

/* Button.module.css */
.button {
  padding: 10px 20px;
  font-size: 16px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.primary {
  background-color: #0070f3;
  color: white;
}

.secondary {
  background-color: #eaeaea;
  color: #333;
}

Button.js

import React from 'react'
import styles from './Button.module.css';

const Button = ({ label, variant = 'primary' }) => {
	return (
		<button className={`${styles.button} ${styles[variant]}`}>{label}</button>
	)
}

export default Button

This approach dynamically applies the primary or secondary class based on the variant prop.

page.js

import Button from "./Button/Button";

export default function Home() {
  return (
    <>
		<Button label="Click Me"/>
    </>
  );
}
css-modules-img-1

Here are examples demonstrating best practices and how to use CSS Modules effectively:

Create a CSS Module file named Button.module.css:

/* Button.module.css */
.button {
  background-color: #0070f3;
  color: white;
  padding: 10px 20px;
  border-radius: 5px;
  border: none;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.button:hover {
  background-color: #005bb5;
}

Then, import and use the styles in a component:

Button.js

import React from "react";
import styles from "./Button.module.css";

const Button = () => {
  return (
    <>
      <button type="button" className={styles.button}>
        Click Me
      </button>
    </>
  );
};

export default Button;

In this example, .button is scoped only to the Button component, so you don’t have to worry about style conflicts with other buttons.

CSS Modules can also style nested elements within a component:

Card.module.css

/* Card.module.css */
.card {
  border: 1px solid #ddd;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.cardHeader {
  font-weight: bold;
  font-size: 1.2em;
}

.cardContent {
  margin-top: 10px;
}

Card.js

import React from 'react'
import styles from "./Card.module.css";
const Card = ({ title, content }) => {
  return (
    <div className={styles.card}>
      <div className={styles.cardHeader}>{title}</div>
      <div className={styles.cardContent}>{content}</div>
    </div>
  );
};

export default Card

page.js

import Card from "./components/Card";

export default function Home() {
  return (
    <>
		<Card title="Card title" content="Card content" />
    </>
  );
}

Here, cardHeader and cardContent are scoped to Card, making it easy to maintain without affecting other components.

For styles that should apply globally (like resets or fonts), you can use a global CSS file.

globals.css(Next.js):

/* globals.css */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: Arial, sans-serif;
}

In pages.js, import the global styles:

import "./globals.css";


export default function Home() {
  return (
    <>
      <h1>Hello World</h1>
    </>
  );
}

You can now use globals.css alongside your CSS Modules, combining global resets with scoped styling in your components.

In this example we will pass props to display a message and style a button.

Button.js

import React from 'react';

export default function Button({ label, color }) {
  return <button style={{ backgroundColor: color }}>{label}</button>;
}

Here:

  • The Button component takes two props: label (text displayed on the button) and color (background color).
  • The values of label and color are set by the parent component.

In the parent component:

page.js

import React from 'react';
import Button from './Button';

export default function Home() {
  return (
    <div>
      <Button label="Click Me" color="blue" />
      <Button label="Submit" color="green" />
    </div>
  );
}

Here, we render two Button components with different label and color values. This makes Button reusable and customizable.

You can use classnames to apply multiple classes conditionally. Install it first:

npm install classnames

Then, use it like this:

Status.module.css

.success {
  color: green;
}

.error {
  color: red;
}

StatusMessage.js

import React from 'react'
import styles from "./Status.module.css";
import classNames from "classnames";

const StatusMessage = ({ status }) => {
  const statusClass = classNames({
    [styles.success]: status === "success",
    [styles.error]: status === "error",
  });
  return <p className={statusClass}>This is a {status} message</p>;
};

export default StatusMessage

This applies either the success or error class based on the status prop.

You can add animations and transitions within CSS Modules too.

Notification.js

import React from "react";
import styles from "./Notification.module.css";

const Notification = ({ message }) => {
  return <div className={styles.notification}>{message}</div>;
};

export default Notification;

Notification.module.css

.notification {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f0f0f0;
  opacity: 0;
  animation: fadeIn 0.5s forwards;
}

@keyframes fadeIn {
  to {
    opacity: 1;
  }
}

The fadeIn animation will only apply to the Notification component’s .notification class, providing scoped animations.

CSS Modules are ideal for:

  • Component-specific styles in React or Next.js projects.
  • Applications where maintainability and modularity are essential.
  • Teams where multiple developers are working on the same codebase.

However, if your project requires extensive dynamic styling or theming, you might consider a CSS-in-JS library like Styled Components or Emotion.

  • Name Your Classes Meaningfully: Use clear, descriptive class names to make your styles easier to understand.
  • Combine Global and Local Styles: Use CSS Modules for component-specific styles and a global CSS file for resets and shared styles like typography.
  • Organize Your Files: Store the CSS Module file alongside the component it styles for better discoverability.
  • Leverage Preprocessors: Combine CSS Modules with Sass or PostCSS for variables, nesting, and other advanced features.
  • CSS Modules (by Clinyong): Provides autocomplete and syntax highlighting specifically for CSS Modules, which is great for quickly writing and referencing classes.
  • CSS Peek: This extension lets you “peek” and “go to definition” for class names in CSS Modules, improving navigation.

CSS Modules offer a straightforward, scalable way to style your applications, making them a great choice for developers seeking a balance between simplicity and modularity.

By embracing this approach, you can create well-organized, conflict-free styles that align perfectly with modern component-based development.

Are you already using CSS Modules in your projects? Share your experiences or questions in the comments below!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Similar articles you may like

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.

Add comment

Stay Updated

Want to be notified when our article is published? Enter your email address below to be the first to know.

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.