Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS class not applying when using SASS in Webpack(5)/React project with Typescript

I have this new project being assembled with Webpack 5 + React 16 + TypeScript 4 and willing to use SASS. I have set all loaders, but yet the class does not apply on the html elements, when I say "class" it's exactly what I mean, if I style directly an element it does work.

I my main.scss main file (which is imported in the main app file App.tsx) if I put:

body { background-color: red; }

This works Ok!

But if I try to set the style using class like this:

.main-body-style {
   background-color: red;
}
...
<body className="main-body-style">...</body>

This doesn't work, super weird!!

My very basic webpack.config.ts:

import path from "path";
import { Configuration, DefinePlugin } from "webpack";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";

import pjson from "./package.json";

const now = new Date();

const buildVersion = `v${pjson.version}-${
  now.getDate().toString().padStart(2, "0") +
  (now.getMonth() + 1).toString().padStart(2, "0") +
  now.getFullYear() +
  "." +
  now.getHours().toString().padStart(2, "0") +
  now.getMinutes().toString().padStart(2, "0")
}`;

const urlStoreMessage = "htpp://";

const __ = (argToStringfy: string) => JSON.stringify(argToStringfy);

const config: Configuration = {
  entry: "./src/index.tsx", 
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader", 
          options: {
            presets: [
              "@babel/preset-env",
              "@babel/preset-react",
              "@babel/preset-typescript",
            ],
          },
        },
      },

      {
        test: /\.s(a|c)ss$/,
        include: [path.resolve(__dirname, "src/assets")],
        use: [
          "style-loader", 
          {
            loader: "css-loader", 
            options: { modules: true },
          },
          "sass-loader", 
        ],
      },          
    ],
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      async: false,
      eslint: {
        files: "./src/**/*.{ts,tsx,js,jsx}", //files: "./src/**/*",
      },
    }),

    new DefinePlugin({
      "process.env": process.env.production || !process.env.development,
      BUILD_VERSION: __(buildVersion),
      URL_STORE_MESSAGE: __(urlStoreMessage),
    }),
  ],
  resolve: {
    extensions: [".tsx", ".ts", ".js"], 
  },
  output: {
    path: path.resolve(__dirname, "build"), 
    filename: "bundle.js", 
  },
  devtool: "source-map",
  devServer: {
    static: path.join(__dirname, "build"),
    compress: true,
    liveReload: true,
    open: false,
    port: 4000,
  },
};

export default config;

Basic .babelrc:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ]
  ]
}

Basic tsconfig.json:

{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "strict": true,
    "sourceMap": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true, // Babel is going to generate the typescript code
    "jsx": "react", //
    "module": "commonjs"
  },
  "include": ["src"]
}

.eslintrc.json:

{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": ["@typescript-eslint", "react-hooks"],
  "extends": [
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "react/prop-types": "off"
  },
  "settings": {
    "react": {
      "pragma": "React",
      "version": "detect"
    }
  }
}

package.json:

...
 "dependencies": {
    "axios": "^0.24.0",
    "bootstrap": "^4.4.1",
    "history": "^5.1.0",
    "react": "^16.8.6",
    "react-bootstrap": "^1.6.1",
    "react-bootstrap-icons": "^1.6.1",
    "react-bootstrap-table": "^4.3.1",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.2.0",
    "sass": "^1.43.4"
  },
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/plugin-transform-runtime": "^7.16.0",
    "@babel/preset-env": "^7.16.0",
    "@babel/preset-react": "^7.16.0",
    "@babel/preset-typescript": "^7.16.0",
    "@babel/runtime": "^7.16.0",
    "@types/bootstrap": "^5.1.6",
    "@types/fork-ts-checker-webpack-plugin": "^0.4.5",
    "@types/node-sass": "^4.11.2",
    "@types/react": "^16.8.6",
    "@types/react-bootstrap": "^0.32.28",
    "@types/react-dom": "^16.8.6",
    "@types/react-router-dom": "^5.3.2",
    "@types/webpack": "^5.28.0",
    "@types/webpack-dev-server": "^4.3.1",
    "@typescript-eslint/eslint-plugin": "^5.3.0",
    "@typescript-eslint/parser": "^5.3.0",
    "babel-loader": "^8.2.3",
    "css-loader": "^6.5.1",
    "css-modules-typescript-loader": "^4.0.1",
    "eslint": "^7.32.0",
    "eslint-plugin-react": "^7.26.1",
    "eslint-plugin-react-hooks": "^4.2.0",
    "fork-ts-checker-webpack-plugin": "^6.4.0",
    "html-webpack-plugin": "^5.5.0",
    "node-sass": "^6.0.1",
    "sass-loader": "^12.3.0",
    "style-loader": "^3.3.1",
    "ts-node": "^10.4.0",
    "typescript": "^4.4.4",
    "webpack": "^5.62.1",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.4.0"
  }
...

And in a very basic App.tsx file:

import React from "react";
import TopBarMenu from "../components/TopBarMenu";

import "../assets/styles/main.scss";

const App: React.FC = () => {
  return (
    <>
      <TopBarMenu />
    </>
  );
};

export default App;

What am I missing here? I have other projects with Webpack and React, but without Typescript and with Webpack 4.

like image 779
Francisco Souza Avatar asked Oct 14 '25 08:10

Francisco Souza


1 Answers

In webpack.config.ts , your have enabled modules in css-loader options for all css/scss files , and they will be treated as css module files. refer to : https://github.com/css-modules/css-modules for usage. a better implementation is to have 2 rules , one to handle the normal css/scss files and another to handle css/scss modules, like this :

{
    test: /\.module\.(sa|sc|c)ss$/,
    use: [
      'style-loader',
      {
        loader: 'css-loader',
        options: { modules: true }
      },
      'sass-loader'
    ]
  },
  {
    test: /\.(sa|sc|c)ss$/,
    exclude: /\.module\.(sa|sc|c)ss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
  },

then all *.scss/css/sass files will be treated as normal , and all *.module.scss/css/sass will be treated as css modules

like image 83
Firas Alkhatib Avatar answered Oct 16 '25 20:10

Firas Alkhatib