13

I have a few SCSS theme files I want to extract to CSS files and later load them into the page. I want to be able to use contenthash for long term caching.

Since I'm using Webpack 4, I am also using mini-css-extract-plugin. I started down the path of creating a splitChunks in my webpack config.

// webpack.config.js
module.exports = {
  plugins: [
      new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].[contenthash].css",
      chunkFilename: "[id].[contenthash].css"
    })
  ],
  optimization: {
    splitChunks: {
      cacheGroups: {
        'vendor': {
            // custom commons chunk for js
        },
        'theme-a': {
            test: /theme-a.\scss/,
        },
        'theme-b': {
            test: /theme-b.\scss/,
        },
        // more themes
      }
    }
  }
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "sass-loader"
        ]
      }
    ]
  }
}

I've then tried dynamically importing the css in my app:

// app.js
class App extends React.Component {
  // constructor

  login(themeName) {
    import(/* webpackChunkName: "`${themeName}`" */ `./path/to/${themeName}.scss`).then(theme => {
      // do something with `theme`
    }
  }

  // other stuff
}

I need to be able to load that css file dynamically in login() and I'm just not sure how to reference it when it has a generated [contenthash].

TLDR: Is there a good way to both extract css and import the referenced CSS bundle to lazy load later? I'm not tied to mini-css-extract-plugin.

Edit: Created a mini-css-extract-plugin issue here.

2 Answers 2

9

My solution ended up using extract-text-webpack-plugin. My config now looks like this:

// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ExtractThemeA = new ExtractTextPlugin({ filename: 'themeA.[hash].css', allChunks: true});

module.exports = {
  plugins: [
      ExtractThemeA,
      // more plugins for other css files
  ],
  optimization: {
    splitChunks: {
      cacheGroups: {
        // Note: No changes to splitChunks
        'vendor': {
            // custom commons chunk for js
        }
    }
  }
  module: {
    rules: [
      {
        test: /theme-a\.scss$/,
        use: ExtractThemeA.extract([ 'css-loader', 'sass-loader' ])
      },
      // more module rules for each css file needed
    ]
  }
}

Then, these chunks are available by file name in my HtmlWebpackPlugin:

<!-- HtmlWebpackPlugin Template -->
<script>
// provides me with an array of file name strings
var themesManifest = <%= htmlWebpackPlugin.files.css %>
</script>
Sign up to request clarification or add additional context in comments.

Comments

5
+25

Sorry for my miss understanding,

You could probably just make two different scss-files and import them as needed. theme.scss admin.scss or like so

This is how I am doing scss in React right now

In App.js

import styles from '../../stylesheet/main.scss'
// could be as well
import styles1 from '../../stylesheet/theme.scss' // some file
import styles2 from '../../stylesheet/admin.scss' // some other file

const App = () => {
  <div className={styles.action_feed} >{ content }</div>
} 

In main.scss

.action_feed {
  position: fixed;
  width: 350px;
  height: auto;
  max-height: 100%;
  max-width: 100%;
  top: 0;
  left: 0;
  z-index: 9999;
}

I think you could just as well do it like so

const themeName = 'main'
import(`./stylesheet/${themeName}.scss`, (css) => {
  // meaby set this to state? not quite sure how should properly handle
  // dynamically imported css
  this.setState({ css: css.action_feed })

  // or possible this way. I have done some non-React dom manipulation
  // this way before
  document.querySelector('body').classList.add(css.action_feed)
})

<div className={this.state.css}>{ content }</div>

You should probably check out React's new Refs API as well. It might give you some nice flexibility for giving className-attr to required element.

Having set to splitChunks.chunks to all works though i think in this case anyway

2 Comments

I'm not worried about my JS not being chunked, but these individual theme css files. I need these files extracted and loaded on login()
I appreciate the response, but again, not what I was looking for. I need the css extracted (for http caching purposes) and dynamically injected into the page on login().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.