How do I include node_modules css files?

I’m having a hard time figuring this out. I have all these node_modules which include the css files I need but I can only get the javascript from the require.

For example require(‘bootstrap’) will only return the js file and not the css.

I’ve tried

require("!style!css!bootstrap/dist/css");

but I just get this error:

ERROR in ./app/js/loader.js
Module not found: Error: Cannot resolve module ‘bootstrap.css’ in C:\Users\Administrator\Dropbox\projects\spDash\app\js
@ ./app/js/loader.js 10:0-24

ERROR in ./app/js/loader.js
Module not found: Error: Cannot resolve module ‘bootstrap/dist/css’ in C:\Users\Administrator\Dropbox\projects\spDash\app\js
@ ./app/js/loader.js 19:0-40

My config looks like this:

var webpack = require('webpack')
module.exports = {
  entry: {
    'spdash': './app/js/loader.js'
  },
  output: {
    filename: 'bundle.js',
  },
  devtool: 'source-map',
  module: {
    loaders: [
      // the url-loader uses DataUrls.
      // the file-loader emits files.
      { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
      { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
      {
        test: /\.css$/, // Only .css files
        loader: 'style!css' // Run both loaders
      },
      { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
      { test: /\.css$/, loader: 'style-loader!css-loader' },
      {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // inline base64 URLs for <=8k images, direct URLs for the rest
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      minimize: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/])
  ]
};

Author: Fantashit

11 thoughts on “How do I include node_modules css files?

  1. Try require("!style!css!bootstrap/dist/css/bootstrap.css"). Also, you don’t need !style!css! since you already have that specified in the loaders configuration.

  2. @ArdentKid it’s same as including a local CSS file. You include npm modules with require('some-module'), so if that module has a CSS file somewhere, you would do require('some-module/path/to/style.css'). Make sure you have a CSS loader defined.

  3. @import 'bourbon' won’t work because a) instead of the main SCSS, bourbon has index.js defined as main (not sure what that index.js is supposed to be), and b) because modules should be imported with the ~ prefix, try @import '~bourbon/core/_bourbon.scss'.

    Concerning normalize-css, try installing normalize.css instead and doing require('normalize.css').

    Let me know if that works.

  4. Thank you @silvenon for replying so quickly; I got it to work!

    I put this in the stylesheet at ./src/components/Foo/style.scss:

    @import '~bourbon/app/assets/stylesheets/_bourbon';
    @import '~normalize.css/normalize';
    

    I am resolving extensions via resolve.extensions in ./webpack.config.js:

    resolve: {
        extensions: ['', '.js', '.css', '.scss']
    }
    

    UPDATE:

    I can make my life easier with aliases.

    ./webpack.config.js:

    resolve: {
        extensions: ['', '.js', '.css', '.scss'],
        alias: {
            bourbon: path.join(__dirname, '/node_modules/bourbon/app/assets/stylesheets/_bourbon.scss'),
            normalize: path.join(__dirname, '/node_modules/normalize.css')
        }
    }
    

    ./src/components/Foo/style.scss:

    @import 
    '~bourbon',
    '~normalize';
    
  5. I didn’t get any errors, and the Normalize code wasn’t showing up in bundle.css.

    It won’t end up in the bundle.css because this is the loader you’re using for CSS:

    {
      test: /\.css$/,
      loader:  'style!css'
    },

    Normalize should end up inside <style> tags on your page, please check if that happened.

    Because you’re using CSS Modules, you’ll want better control over which stylesheets are processed as CSS Modules and which aren’t. I had this problem as well and this is what I’ve come up with, I modified it according to your config:

    // ...
    const resolve = require('path').resolve;
    
    // this part is completely up to you,
    // I like to set up my environment like this
    const IS_DEV = !process.env.NODE_ENV;
    const IS_PROD = process.env.NODE_ENV === 'production';
    
    // let's extract this logic here
    const LOCAL_IDENT_NAME = IS_DEV ?
      'bundle-scss__[local]-class___[hash:base64:3]' :
      'mk-[hash:base64:5]';
    
    // ...
    
    {
      test: /\.s?css$/, // selects BOTH .scss and .css files
      loader: IS_DEV ?
        `style!css?modules&localIdentName=${LOCAL_IDENT_NAME}!sass` :
        ExtractTextPlugin.extract('style', 'css?modules!sass'),
      include: [
        // only stylesheets inside src/styles and the react-toolbox module
        // will be processed as CSS Modules
        resolve('./src/styles'),
        /react-toolbox/,
      ],
      exclude: [
        // EXCEPT for stylesheets in src/styles/global
        resolve('./src/styles/global'),
      ],
    },
    {
      test: /\.s?css$/,
      loader: IS_DEV ?
        'style!css!sass' :
        ExtractTextPlugin.extract('style', 'css!sass'),
      include: [
        // stylesheets in node_modules and src/styles/global
        // will be treated as global, i.e. not CSS Modules
        resolve('./node_modules'),
        resolve('./src/styles/global'),
      ],
      exclude: [
        // EXCEPT for react-toolbox, which should be treated as CSS Modules
        // as specified by the previous loader
        /react-toolbox/,
      ],
    },
    // ...

    This way you can add src/styles/global/_base.scss, which overrides some things in Normalize.css, and all classes there will be global, without having to do all the :global() nonsense. React Toolbox is just an example here, in case you ever have to install a module and treat it as CSS Modules.

    As you said, webpack is not that easy. 😕

    EDIT: I made a lot of edits to this comment, hopefully you didn’t start reading right away. 😅

  6. Thank you for all your help, @silvenon! I adopted the “two loader approach” to match the files with the desired loader. You were right about Normalize being rendered inline, too. Didn’t catch that!

    TL;DR

    Thank you! If you’d like to see what I did, check out my repository.

    Long explanation

    Here is a brief summary of my new project setup:

    root
        src
            components
                Foo
                    foo.scss (`@import '~utils'`)
                    index.js
            scss
                globals (Global styles that go before CSS module styles)
                utils (Bourbon, Sass mixins, variables, etc. that won't be outputted)
            app.js (entry point)
    

    Here is the Webpack config file:

    ...
    
    const IS_DEV = process.env.NODE_ENV !== 'production';
    
    const LOCAL_IDENT_NAME = IS_DEV ? 
        '[name]-scss__[local]-class___[hash:base64:2]' 
    : '[hash:base64:2]';
    
    const globals = [
        resolve('./node_modules/normalize.css'), // any NPM module
        resolve('./src/scss/globals'), // global CSS classes
    ];
    
    const cssModules = [
        resolve('./src/components'), // CSS modules
    ];
    
    ...
    
    entry: {
        bundle: [
            './node_modules/normalize.css',
            './src/scss/globals',
            './src/app'
        ]
    },
    output: {
        path: path.join(__dirname, 'public'),
        filename: IS_DEV ? 'js/[name].js' : 'js/[name].min.js',
        publicPath: '/'
    },
    module: {
        loaders: [
            // loader for CSS modules
            {
                test: /\.s?css$/,
                loader:  ExtractTextPlugin.extract('style', `css?modules&localIdentName=${LOCAL_IDENT_NAME}!sass`),
                include: cssModules,
                exclude: globals
            },
            // loader for global styles
            {
                test: /\.s?css$/,
                loader:  ExtractTextPlugin.extract('style', 'css!sass'),
                include: globals,
                exclude: cssModules
            }
        ]
    },
    resolve: {
        extensions: ['', '.js', '.css', '.scss'],
        alias: {
            bourbon: path.join(__dirname, '/node_modules/bourbon/app/assets/stylesheets/_bourbon.scss'),
            utils: path.join(__dirname, '/src/scss/utils')
        }
    }
    
  7. For anyone trying to import node_modules packages from SCSS, prefix with ~:

    @import '~semantic-ui-css/semantic.css';
    
  8. For any one else who might have trouble with webpack 4 while using css-loader with css-modules and are only using scss files, I was able to get this config to work:

    webpack.dev.js

    const resolve = require('path').resolve
    
    const globalCSS = resolve(__dirname, '../', 'src/styles/globals'),
    
    module.exports = {
      ...
      module: {
        rules: [
          {
            test: /\.s?css$/, // if stylesheet is CSS or SCSS
            exclude: [globalCSS], // exclude global styles (styles with node_module imports)
            use: [
              'style-loader',
              {
                loader: 'css-loader',
                options: {
                  sourceMap: true,  // allow source mapping
                  modules: true, // allow module imports (ex: import styles from 'example.scss')
                  localIdentName: '[local]___[hash:base64:5]', // allow class hash renaming
                },
              },
              'sass-loader',
            ],
          },
          {
            test: /\.s?css$/, // if stylesheet is CSS or SCSS
            include: [globalCSS], // only include globally imported styles (ex: @import '~material-icons/iconfont/material-icons.scss'; -- can be an imported CSS stylesheet as well)
            use: [
              'style-loader',
              'css-loader', // no source map, no css module imports and no class renaming
              'sass-loader',
            ],
          },
        ],
      },
      ...
    }
    
    

    Example on how to use:

    src/styles/globals/globals.scss

    @import '~bootstrap/dist/css/bootstrap.min.css';
    @import '~font-awesome/css/font-awesome.css';
    

    src/styles/variables/_variables.scss

    $navyblue: #114678;
    

    src/components/App/App.scss

    @import '../../styles/variables/variables';
    
    App {
      color: $navyblue;
    }
    

    src/components/App/App.js (globals.scss can be imported in src/index.js instead)

    import { App } from './App.scss';
    import '../../styles/globals/globals.scss';
    
    <div className={App}>
      <h1>Example</h1>
      <button className="btn btn-primary">
        Button
      </button>
      <i className="fa fa-fort-awesome" aria-hidden="true" />
    </div>
    

Comments are closed.