How to apply global styles with CSS modules in a react app?
Asked Answered
M

7

128

I'm currently using CSS Modules with React for my styling. So each of my components is importing in it's component specific css file, like so:

import React from 'react';
import styles from './App.css';

const example = () => (
  <div className={styles.content}>
    Hello World!
  </div>
);

export default example;

This works fine when styling individual components, but how do I apply global styling (html, body, header tags, divs, etc.) that isn't component specific?

Morrell answered 1/10, 2016 at 10:31 Comment(2)
Why not to require('./App.css'); in your root component?Orose
@Orose I'm not the author but in my case it's a multi site (so needs different global css per website) + I want to style WYSIWYG content which is set inside dangerouslySetInnerHTMLHost
A
214

Since you're using the ES6 import syntax you may use the same syntax to import your stylesheet

import './App.css'

Also, you can wrap your class with :global to switch to the global scope (this mean CSS Module won't modulify it, eg: adding a random id next to it)

:global(.myclass) {
  background-color: red;
}
Argentinaargentine answered 16/10, 2016 at 0:3 Comment(6)
For me, classnames from a file imported like import './App.css' just have no effect at all. It only works with your second piece of code. This is my first encounter with this "css modules" thing and I already hate it for breaking things which worked for millenia.Sissified
@MikhailBatcer Same thing happened with me. In fact I have to use named import and then apply classes. import classes from './App.css' and then apply classes.MyclassPrescriptive
@FaizanMubasher Yes, named import worked for me too.Sissified
@Argentinaargentine thx a lot man! That was absolutely what I was looking for!Chrysanthemum
In case anyone is trying to upgrade an older NextJS project to v12+ the :global wrapper is no longer needed.Pearcy
@ScottL I’m on NextJS v13.4.4, and it needs the :global wrapper to target global CSS classes.Beirut
M
11

This can be done by simply adding:

require('./App.css');

(thanks @elmeister who correctly answered this question.)

Morrell answered 6/10, 2016 at 18:39 Comment(2)
Or to avoid mixing es modules with commonjs, just import './App.css'Kincardine
@riscarrott yes but you need named import then as per #39806037Carencarena
K
7

The only way I found for importing styles globally, but only for a specific route is adding:

<style dangerouslySetInnerHTML={{__html: `
  body { max-width: 100% }
`}} />

inside the return of render method.

Otherwise, the style tag would be added to the <head>, and the styles would be applied for all next routes.

From https://medium.learnreact.com/the-style-tag-and-react-24d6dd3ca974

Maybe the styles could be imported as a string from a file to have the code more organized.

Kernan answered 17/4, 2018 at 12:24 Comment(1)
Same question and same successful codes to apply to my case. Thank you!Eeg
K
7

I had encountered the same problem and found the following ways to address the issue, you may choose what applies to you best

  1. Define two sets of rules in your webpack config for parsing css/less files.
    • The first rule should be to include all global styles, assuming it's kept in /styles/ or similar directory.
    • The second rule is to process all locally scoped css styles, these should ideally be next to their components.
    • You can do this by using the include and exclude option while defining a rule
    • or by enforcing a naming convention and writing rule accordingly, for example all css modules will be [name].module.css and your test would check for /.module.(less|css)$/ and parse it.

A sample is given below:

      // exclude all global styles for css modules
      {
        test: /\.(less|css)$/,
        exclude: path.resolve(__dirname, './src/styles'),
        use: [
          {
            loader: CssExtractPlugin.loader,
            options: { hot: is_dev, reloadAll: is_dev }
          },
          {
            loader: "css-loader",
            options: { 
                modules: { 
                    localIdentName: '[local]___[hash:base64:5]'
                }
            }
          },
          "postcss-loader",
          "less-loader"
        ]
      },
      // process global styles without css modules
     {
        test: /\.(less|css)$/,
        include: path.resolve(__dirname, './src/styles'),
        use: [
          {
            loader: CssExtractPlugin.loader,
            options: { hot: is_dev, reloadAll: is_dev }
          },
          "css-loader",
          "postcss-loader",
          "less-loader"
        ]
      }
  1. Use :local and :global while writing css/less. If css modules is enabled it will default to local mode, you can specify the mode in options as below:
          {
            loader: "css-loader",
            options: { 
                modules: { 
                    localIdentName: '[local]___[hash:base64:5]', 
                    mode: 'global',
                }
            }
          },

If you are defining mode as global then all your included css classes will not be replaced with hashed names, instead only the ones you specify as :local will be given unique names. For example:

/* this will remain as is */
.header {
   color: blue;
}

:local {
  /* this will become something like item_xSH2sa */
  .item {
   color: yellow;
  }
}

  1. Define a function which checks your file to decide if it's css module or global, done using getLocalIdent option. This is the method that I'm currently using in my setup. This also requires your files to have some naming convention, [name].module.less for css modules and [name].less for regular files. See example below:
// regex to test for modules, loaderUtils is part of webpack dependencies
const cssModuleRegex = new RegExp(/\.module\.(less|css)$/);
const loaderUtils = require("loader-utils");

// inside webpack rules
      {
        test: /\.(less|css)$/,
        use: [
          {
            loader: CssExtractPlugin.loader,
            options: { hot: is_dev, reloadAll: is_dev }
          },
          {
            loader: "css-loader",
            options: { 
                modules: { 
                    localIdentName: '[local]___[hash:base64:5]', 
                    getLocalIdent: getLocalIdent
                }
            }
          },
          "postcss-loader",
          "less-loader"
        ]
      }

// this is a copy of the default function, modified slightly to achieve our goal
function getLocalIdent(loaderContext, localIdentName, localName, options) {

    // return local name if it's a global css file
    if (!cssModuleRegex.test(loaderContext.resourcePath)) {
        return localName;
    }

    if (!options.context) {
      // eslint-disable-next-line no-param-reassign
      options.context = loaderContext.rootContext;
    }

    const request = path
      .relative(options.context, loaderContext.resourcePath)
      .replace(/\\/g, '/');

    // eslint-disable-next-line no-param-reassign
    options.content = `${options.hashPrefix + request}+${localName}`;

    // eslint-disable-next-line no-param-reassign
    localIdentName = localIdentName.replace(/\[local\]/gi, localName);

    const hash = loaderUtils.interpolateName(
      loaderContext,
      localIdentName,
      options
    );

    return hash
      .replace(new RegExp('[^a-zA-Z0-9\\-_\u00A0-\uFFFF]', 'g'), '-')
      .replace(/^((-?[0-9])|--)/, '_$1');
  }

Krauss answered 1/10, 2019 at 18:39 Comment(1)
Last solution is a charm, thanks for sharing!Whereinto
C
4

The best practice I can say there should be a CSS folder where you can keep all the CSS you need all over the application like I need font size is 20 in my all components so need to follow these step:

  • create a separate folder named css/scss (scss is preferred) .
  • create file name _font.scss.
  • create a file name index.scss.
  • import _font.scss inside index.scss (@import './fonts';).
  • import this in your index.js starting file (import "../scss/index.scss").
  • Now you can use your css all over the application.

In this case, you may find duplication. For example - you have container CSS globally which is created one of your files but you want to have a separate container for one of your file

This case make a separate css/scss file for this application name should be like (cssname.module.scss) must include .module for better handling.

With this css you can import your jsx file (import Style from './cssname.module.scss')

You can use like className= {Style.container} but if you want to use some of your global and local css together install Classnames from npm (npm install classNames)

how to use:

import cn from 'Classnames'
className = {cn(Style.container,"fs-20 text-center")}

here fs-20 and text-center are the global CSS present in some of your files

Christhood answered 8/9, 2021 at 4:17 Comment(1)
Thanks for sharing an answer for the original poster's question. Please be mindful of formatting the answer appropriately. For further help, see the following markdown guide: stackoverflow.com/editing-helpTsan
F
3

Another simple way if you want to use css module is:

<style jsx global>{`
     .slick-prev:before,
     .slick-next:before {
         color: somecolor;
      }
`}</style>
Frulla answered 7/1, 2021 at 20:10 Comment(0)
L
0

Solution 1 :

Import global styles in starting point of reactapp.
where the whole react app has been wrapped in a root component .
it can be index.js or app.js or index.html

Solution 2 :

use scss and create main.scss file and import all other specifically needed custom scss files in main.scss

Lowrey answered 1/10, 2019 at 18:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.