skip to Main Content

For performance improvements i need to separate the CSS from the JS in my Webpack config into it’s own files. E.g style.css and main.js or whatever i chose to call them. Currently I’m importing the css in my main JS-file like import 'style.css'.

How can i change this?

Webpack config.

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const isProduction = process.env.NODE_ENV === 'production';

const stylesHandler = 'style-loader';

const config  = {
  entry: './src/js/index.js',
  output: {
    path: path.resolve(__dirname, './dist/js'),
    filename: 'main.js',
    clean: true, // remove unused bundled files
  },
  module: {
    rules: [
      {
        test: /.(js|jsx)$/i,
        loader: 'babel-loader',
      },
      {
        test: /.css$/i,
        use: [stylesHandler, 'css-loader', 'postcss-loader'],
      },
      {
        test: /.(eot|svg|ttf|woff|woff2|png|jpg|gif|otf)$/i,
        type: 'asset',
      },
    ],
  },
};

module.exports = () => {
  if (isProduction) {
    config.mode = 'production';
  } else {
    config.mode = 'development';
  }
  return config;
};

Package.json (scripts)

  "scripts": {
    "build": "webpack --mode=production --node-env=production",
    "build:dev": "webpack --mode=development",
    "build:prod": "webpack --mode=production --node-env=production",
    "watch": "webpack --watch"
  }

3

Answers


  1. You can use the modern html-bundler-webpack-plugin.

    This plugin renders HTML templates containing references to source files of script, styles, images, etc.

    Very simple usage example:

    <html>
    <head>
      <!-- specify source style files -->
      <link href="./styles.scss" rel="stylesheet">
      <!-- specify source script files here and/or in body -->
      <script src="./main.js" defer="defer"></script>
    </head>
    <body>
      <h1>Hello World!</h1>
      <!-- specify source image files -->
      <img src="./picture.png">
    </body>
    </html>
    

    You can specify script, style and image source files in HTML using a relative path or Webpack aliases.

    const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
    
    module.exports = {
      plugins: [
        new HtmlBundlerPlugin({
          entry: {
              // define many templates here
              index: 'src/views/home.html', // => dist/index.html
            },
          },
          js: {
            // JS output filename, used if inline is false
            filename: 'js/[name].[contenthash:8].js',
            // inline: true, // inlines JS into HTML
          },
          css: {
            // CSS output filename, used if inline is false
            filename: 'css/[name].[contenthash:8].css',
            // inline: true, // inlines CSS into HTML
          },
        }),
      ],
      module: {
        rules: [
          {
            test: /.(css|sass|scss)$/,
            use: ['css-loader', 'sass-loader'],
          },
          {
            test: /.(ico|png|jp?g|webp|svg)$/,
            type: 'asset/resource',
            generator: {
              filename: 'img/[name].[hash:8][ext][query]',
            },
          },
        ],
      },
    };
    

    No additional plugins and loaders required.

    The plugin detects all source files referenced in a template and extracts processed assets to the output directory. Compiled JS and CSS can be inlined into generated HTML using the inline plugin option. In the generated HTML and CSS, the plugin substitutes the source filenames with the output filenames.

    The generated HTML contains the output filenames of the processed files:

    <html>
    <head>
      <link href="css/styles.05e4dd86.css" rel="stylesheet">
      <script src="js/main.f4b855d8.js" defer="defer"></script>
    </head>
    <body>
      <h1>Hello World!</h1>
      <img src="img/picture.58b43bd8.png">
    </body>
    </html>
    

    Open in StackBlitz

    Login or Signup to reply.
  2. You can add the MiniCssExtractPlugin:

    module: {
      rules: [{
        test: /.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ],
      }],
    },
    
    Login or Signup to reply.
  3. If you’re fine with working with scss, you can do something like this:

    1. Create your style index file somewhere at the root of your project: ‘./src/styles/_index.scss’
    @import './variables.scss';
    @import './global.scss';
    @import './sections.scss';

    You want to import the scss for your website, convert it to css and then do some post-processing. In the end it will end up as minified css file in your dist folder. That’s the file you need to serve and import in your html-page.

    1. Change your webpack config:
    const config  = {
      entry: ['./src/js/index.js', './src/styles/_index.scss'],
      output: {
        path: path.resolve(__dirname, './dist'),
        clean: true, // remove unused bundled files
      },
      module: {
        rules: [
          {
            test: /.(js|jsx)$/i,
            loader: 'babel-loader',
            options: { outputPath: 'js/', name: 'main.js' }
          },
          {
                    test: /.scss$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: { outputPath: 'styles/', name: '[name].min.css' }
                        },
                        'sass-loader'
                    ]
          },
          {
            test: /.(eot|svg|ttf|woff|woff2|png|jpg|gif|otf)$/i,
            type: 'asset',
          },
        ],
      },
    };
    1. If you build, you should have a ./dist/styles/_index.min.css file. That’s the file you need to serve. If you’re using express, that’s looks something like this:

      app.use(express.static(path.join(__dirname, './styles')))

    2. import it into your html, eg: <link rel="stylesheet" href="/_index.min.css"/>

    NOTE: you need to keep the bundles css file size in to consideration. You probably want to have an _index.scss file per page (code splitting). If you still end up with too big files sizes you need to go even further. For every page, you can have multiple bundled style.min.css files you can lazy load while scrolling.

    NOTE: You should be able to use the css-loader in your webpack config, probably between the file-loader and the sass-loader.

    You can have a look here where I did something similar: https://github.com/BeeckmanThe1/uploader/blob/main/webpack.config.js

    Hope that helps

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search