0

I'm making a tree shaken component library in React with webpack@5.24.2 and mini-css-extract-plugin@1.3.9 that has to work for client side rendering and server side rendering. I want to be able to do the following, create "N" components in the library, each with their specific "SCSS" files, something like this:

// KhalComponent.tsx
import React from 'react';
import './KhalComponent.scss';

const KhalComponent = () => {
  return <div className="khal-component">Khal Component</div>;
};

export default KhalComponent;

// KhalComponent.scss
.khal-component {
  padding: 2rem;
  background: red;
}

and when an external project does the following: import KhalComponent from 'some-library/KhalComponent' it gets the component along with its styles. I have this working with style-loader but I've read that this isn't a correct configuration for production AND it DOESN'T work with SSR (using NextJS) since with style-loader it tries to inject the styles adding them with document.createElement (document doesn't exist in the server side). So I tried migrating to using mini-css-extract-plugin to achieve this client side AND server side. I see that mini-css-extract-plugin does create the separate CSS files but (as far as I can tell) it doesn't let the corresponding JS file (KhalComponent.js) know that it NEEDS to load the CSS whenever KhalComponent is imported in another project.

I'm not generating an HTML file in this external component library since its just to import components JS and CSS content.

The webpack config is the following:


const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const entries = require('./entries');
const LIB_NAME = 'lib';

module.exports = {
  entry: entries,
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, LIB_NAME),
  },
  plugins: [new CleanWebpackPlugin(), new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.s?css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
    ],
  },
  externals: [
    {
      react: 'react',
      'react-dom': 'react-dom',
    },
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  devtool: 'source-map',
  devServer: {
    contentBase: './lib',
    hot: true,
  },
};

and entries.js is:

const path = require('path');

const SRC_DIR = path.resolve('src');

module.exports = {
  // Components
  KhalComponent: path.join(SRC_DIR, 'KhalComponent'),
  LinkComponent: path.join(SRC_DIR, 'LinkComponent'),
};


What am I missing or what tools should I use to do this? Thank you very much in advance!

  • Possibly, you need `sideEffects: true` in your rule with `test: /\.s?css$/`? – Steve Jorgensen Sep 24 '21 at 21:31
  • I am trying the same thing, I want to extract the css files but I also want to be able to import the styles using the js modules. and although they are generated they are all empty. Please post an answer if you found a solution on your own. – Ricardo Silva Jan 04 '22 at 04:12

1 Answers1

0

I did a lot of research for this and the best solution I came up with was to just use mini-css-extract-plugin to separate out the css bundle (as I want to give user the functionality to add custom css in the form of styles) and port all imports through a parent index.js file.

Directory would look something like this:

|-build/
|--main.min.js
|--main.min.css
|-index.js

And your index.js content would look something like this:

export {default} from './build/main.min.js';
export * from './build/main.min.js';
import './index.css';

Hope it helps...