Skip to main content

Next.js CSS Modules

Introduction

CSS Modules are a powerful styling solution available out of the box in Next.js that allows you to write component-scoped CSS. Unlike traditional CSS where styles are global, CSS Modules automatically create unique class names, ensuring your styles don't clash with other components. This makes CSS Modules an excellent approach for component-based architecture in modern web applications.

In this tutorial, you'll learn how to implement CSS Modules in your Next.js project, understand their advantages, and see real-world examples of how they can streamline your styling workflow.

What are CSS Modules?

CSS Modules are regular CSS files that are locally scoped to components by default. When you import a CSS Module into a component, it creates unique class names, preventing style conflicts across your application.

Key Features of CSS Modules

  • Local Scoping: Styles are scoped to specific components
  • Name Uniqueness: Class names are automatically made unique
  • No Runtime Overhead: Compiled at build time, not runtime
  • Works with existing CSS: You can use all standard CSS features
  • Built-in Support: No additional configuration needed in Next.js

Getting Started with CSS Modules in Next.js

Creating a CSS Module

To create a CSS Module in Next.js, simply name your CSS file with the .module.css suffix.

For example, let's create a button component with its own styles:

  1. Create a CSS Module file named Button.module.css:
css
.button {
padding: 10px 15px;
background-color: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}

.button:hover {
background-color: #0051a2;
}

.buttonLarge {
font-size: 20px;
padding: 12px 20px;
}
  1. Use the CSS Module in your React component:
jsx
import styles from './Button.module.css';

export default function Button({ children, size }) {
return (
<button
className={size === 'large' ? `${styles.button} ${styles.buttonLarge}` : styles.button}
>
{children}
</button>
);
}

When Next.js builds your application, it will transform the class names to be unique, something like:

html
<button class="Button_button__2Xguw Button_buttonLarge__1A3Cv">
Click Me
</button>

Using CSS Modules with Multiple Classes

There are several ways to apply multiple CSS classes when using CSS Modules:

String Concatenation

jsx
<div className={`${styles.card} ${styles.highlighted}`}>
Content here
</div>

Conditional Classes

jsx
<div className={`${styles.card} ${isActive ? styles.active : ''}`}>
Content here
</div>

Using the classnames Library

For more complex conditional class logic, the popular classnames library works well with CSS Modules:

First, install the library:

bash
npm install classnames

Then use it in your component:

jsx
import classNames from 'classnames';
import styles from './Card.module.css';

export default function Card({ isActive, isHighlighted, children }) {
return (
<div
className={classNames({
[styles.card]: true,
[styles.active]: isActive,
[styles.highlighted]: isHighlighted
})}
>
{children}
</div>
);
}

Global Styles with CSS Modules

While CSS Modules are primarily for local, component-scoped styles, you can also define global styles within them using the :global syntax:

css
/* Button.module.css */
.button {
/* Local styles as before */
}

:global(.btn-wrapper) {
margin: 20px 0;
display: flex;
justify-content: center;
}

The .btn-wrapper class will be available globally in your application.

Composing CSS Modules

CSS Modules support composition, allowing you to reuse styles across different modules:

css
/* colors.module.css */
.primary {
color: #0070f3;
}

.secondary {
color: #6c757d;
}
css
/* Button.module.css */
@value primary, secondary from './colors.module.css';

.buttonPrimary {
composes: button;
background-color: primary;
}

.buttonSecondary {
composes: button;
background-color: secondary;
}

Real-world Example: Building a Card Component

Let's build a reusable card component using CSS Modules:

  1. Create Card.module.css:
css
.card {
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}

.card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}

.cardTitle {
font-size: 1.5rem;
margin-top: 0;
margin-bottom: 10px;
color: #333;
}

.cardContent {
color: #666;
line-height: 1.6;
}

.featured {
border-left: 5px solid #0070f3;
background-color: #f8f9fa;
}
  1. Create a Card component:
jsx
import styles from './Card.module.css';
import classNames from 'classnames';

export default function Card({ title, children, featured = false }) {
return (
<div className={classNames(styles.card, { [styles.featured]: featured })}>
<h2 className={styles.cardTitle}>{title}</h2>
<div className={styles.cardContent}>
{children}
</div>
</div>
);
}
  1. Use the Card component in a page:
jsx
import Card from '../components/Card';

export default function HomePage() {
return (
<div>
<h1>Welcome to My Next.js Site</h1>

<Card title="Getting Started with Next.js">
Next.js provides a great developer experience with features like
server-side rendering and static site generation.
</Card>

<Card title="Featured Tutorial" featured={true}>
Learn how to build scalable React applications with Next.js and
style them using CSS Modules.
</Card>
</div>
);
}

Debugging CSS Modules

When debugging CSS Modules in development mode, Next.js preserves the original class names by prefixing them with the component and file name. For example, .button might become .Button_button__2Xguw, making it easier to identify the source of styles in your browser's developer tools.

CSS Modules vs. Other Styling Solutions

FeatureCSS ModulesInline StylesGlobal CSSStyled Components
Scoped styles
No runtime overhead
Pre-processor support
Dynamic styling
Built into Next.js
Media queries

Best Practices for CSS Modules in Next.js

  1. Keep modules small: Focus on component-specific styles
  2. Organize by component: Store CSS Modules alongside their components
  3. Use descriptive class names: While they'll be transformed, clear names help with development
  4. Avoid global styles: Use the global scope sparingly, if at all
  5. Leverage composition: Reuse styles through composition when possible
  6. Use utility libraries: Consider classnames for complex conditional styling logic

Summary

CSS Modules in Next.js provide an excellent solution for component-based styling with automatic scoping. They allow you to write regular CSS while avoiding the common pitfalls of global styles, such as class name collisions and specificity issues. With built-in support in Next.js, you can start using CSS Modules without any additional configuration.

Key takeaways:

  • CSS Modules create unique class names automatically
  • They work natively in Next.js with the .module.css naming convention
  • They're perfect for component-based architectures
  • They offer a good balance between isolation and familiarity

Additional Resources and Exercises

Resources

Exercises

  1. Basic Implementation: Create a navbar component using CSS Modules with styles for both desktop and mobile views.

  2. Theme Switching: Build a component that uses CSS Modules and allows switching between light and dark themes.

  3. Component Library: Create a small component library (Button, Card, Alert) using CSS Modules with consistent styling.

  4. Responsive Design: Implement a responsive grid system using CSS Modules with media queries.

  5. Animation Practice: Add transitions and animations to components styled with CSS Modules.

By completing these exercises, you'll gain practical experience with CSS Modules in Next.js and develop a solid foundation for styling your applications.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)