How to Apply Global Styles and Override Defaults in React CSS Modules
Import plain CSS files at your application entry point for global resets and themes, and use the :global selector inside CSS Module files to override external library styles while keeping component styles locally scoped.
React CSS Modules provide automatic local scoping for component styles, preventing class name collisions across your application. When you need to apply application-wide resets, typography, or override styles from third-party components, you must bridge the gap between the module system and global CSS. According to the React source code, the recommended approach relies on specific webpack configuration patterns and strategic file organization.
Understanding the React CSS Modules Architecture
React’s implementation of CSS Modules relies on webpack loader configurations that distinguish between module-scoped and global styles. In the React repository, the fixtures/flight/config/webpack.config.js file demonstrates this dual-rule setup.
The configuration uses two distinct regex patterns:
cssRegexmatches standard.cssfiles for global stylescssModuleRegexmatches.module.cssfiles for local scoping
// fixtures/flight/config/webpack.config.js (excerpt)
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
modules: { mode: 'local', getLocalIdent: getCSSModuleLocalIdent },
}),
},
The modules: { mode: 'local' } setting ensures that every class name in a .module.css file receives a unique hash, while files processed by the non-module rule retain their original selectors.
Recommended Approaches for Global Styles in React CSS Modules
Import Plain CSS at the Application Entry Point
For global resets, typography, and CSS custom properties, import a standard CSS file once in your application root. This file should not use the .module.css extension.
Create your global stylesheet:
/* src/index.css */
html, body {
margin: 0;
font-family: system-ui, sans-serif;
}
:root {
--brand-primary: #0d6efd;
--spacing-unit: 1rem;
}
Import it in your entry component:
// src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'; // Global styles without .module.css extension
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
This approach leverages the non-module webpack rule, preserving your selectors exactly as written and applying them to the entire document.
Using CSS Custom Properties for Theming
Define theme variables in your global stylesheet and consume them within CSS Module files. This maintains the cascade for theming while keeping component structures locally scoped.
/* src/components/Button.module.css */
.button {
background: var(--brand-primary);
color: white;
padding: calc(var(--spacing-unit) * 0.5) var(--spacing-unit);
border: none;
border-radius: 4px;
}
Variables cascade through the DOM, so any component reading them reflects theme changes without breaking module scope.
Overriding Default Component Styles in React CSS Modules
The :global Selector for Targeting External Classes
When you need to override styles from a third-party library or target elements rendered by a component, use the :global selector inside your CSS Module file. This tells the CSS Modules loader to emit the selector unchanged.
/* src/components/Overrides.module.css */
/* Override Material-UI default button radius */
:global(.MuiButton-root) {
border-radius: 0;
text-transform: none;
}
/* Target a specific global utility class while keeping local scope for .container */
.container {
composes: layout-grid from global;
padding: 2rem;
}
In your React component, you only need to apply the local class:
// src/components/CustomButton.jsx
import React from 'react';
import styles from './Overrides.module.css';
export default function CustomButton({ children }) {
return (
<button className={styles.container}>
{children}
</button>
);
}
Composing Classes for Library Component Wrappers
When wrapping a library component that exports its own CSS Module classes, compose the library's hashed class with your local customizations.
/* src/components/CustomCard.module.css */
.customCard {
border: 2px solid var(--brand-primary);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
// src/components/CustomCard.jsx
import React from 'react';
import libStyles from 'some-ui-library/Card.module.css';
import localStyles from './CustomCard.module.css';
export default function CustomCard({ children }) {
return (
<div className={`${libStyles.card} ${localStyles.customCard}`}>
{children}
</div>
);
}
This preserves the library's local scope while allowing your additions to remain scoped to your wrapper component.
Real-World Implementation in the React Repository
The React repository demonstrates these patterns in fixtures/view-transition/src/components/Page.js. This component imports both a global stylesheet and a CSS Module to handle different styling concerns.
// fixtures/view-transition/src/components/Page.js (excerpt)
import './Page.css'; // Global stylesheet for base styles
import transitions from './Transitions.module.css'; // Locally-scoped animations
// Usage within the component
<div className={transitions['enter-slide-right']}>
Content here
</div>
The global Page.css file defines base typography and resets that apply to the entire page, while Transitions.module.css provides hashed animation classes that prevent collision with other components' transition definitions.
Summary
- Import global CSS once at your application entry point (e.g.,
src/index.jsx) for resets, fonts, and CSS variables, ensuring the file does not use the.module.cssextension. - Use
:global()selectors inside CSS Module files to target external library classes or define utility classes that must remain unhashed. - Compose classes when wrapping library components to combine their hashed selectors with your local customizations.
- Define theme variables in global CSS and consume them in modules via
var()to maintain cascading themes without breaking local scope. - Configure webpack with separate rules for
cssRegexandcssModuleRegexto ensure proper processing of global versus local styles, as implemented in the React repository.
Frequently Asked Questions
Can I use React CSS Modules with CSS preprocessors like Sass?
Yes, React CSS Modules work with Sass, Less, and other preprocessors. Configure your webpack loader to process .module.scss or .module.less files through both the preprocessor loader and css-loader with modules: true. The hashing logic applies to the final CSS output, allowing you to use nesting, mixins, and variables within your locally scoped files.
How do I override styles from a third-party UI library in React CSS Modules?
Use the :global selector inside your CSS Module to target the library's static class names. For example, :global(.MuiButton-root) { border-radius: 0; } overrides Material-UI's default button styling. Alternatively, wrap the library component and compose its exported class names with your local classes using template literals: className={`${libStyles.button} ${localStyles.custom}`}.
Is there a performance difference between global CSS and CSS Modules?
CSS Modules introduce negligible build-time overhead due to class name hashing and scope analysis, but runtime performance is identical to global CSS. Both approaches result in standard CSS rules applied to the DOM. However, CSS Modules can improve runtime performance in large applications by eliminating selector collisions and unused global styles, allowing for more aggressive code splitting and tree shaking of component styles.
How do I share common styles between components using React CSS Modules?
Use the composes keyword within your CSS Module to inherit styles from another module. For example: .button { composes: base-button from './BaseButton.module.css'; }. This creates a dependency graph that webpack resolves, ensuring both class names are applied to the element while maintaining local scope. Alternatively, define shared CSS custom properties in a global stylesheet and reference them in multiple module files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →