cssreactjstypescriptwebpackcss-modules

How can I import CSS Modules in a TypeScript file?


I've made some edits which are listed below.

I am working with a combination of React (18.2.0), Typescript (4.8.4), and Webpack (5.75.0). I am attempting to add some styling to my application, and am trying to use CSS modules. I have tried a few Webpack configurations, and none of them are causing Webpack to use the correct loader. I have tried a few other variants of loaders (as seen in the commented out sections), and none of them are working.

I am using an approach similar to this answer to import the stylesheets (declaring a src/Globals.d.ts, setting the typescript-plugin-css-modules compiler plugin for tsc, and importing as import * as styles from './styles.module.css')

Stylesheet import

import * as styles from './ResultCount.module.css'

export const ResultCount = () => {
  const orders = useSelector((state: ReducedState) => state.all.orders)
  console.log('result count style name', styles.results) // always undefined

  return <p className={styles.results}>

src/Globals.d.ts

declare module '*.module.css'

This is the error that I get, regardless of the loader configuration that I use

cached modules 1.59 MiB (javascript) 27.4 KiB (runtime) [cached] 131 modules
./src/components/Order/styles.module.css 96 bytes [built] [1 error]

ERROR in ./src/components/Order/styles.module.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> .order {
...

Would someone be able to tell me what I am doing wrong in my webpack.config.ts file?

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: {
          transpileOnly: true
        },
        exclude: /dist/
      },
      {
        test: /\.css$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: "[name]_[local]_[hash:base64]",
              sourceMap: true,
              minimize: true
            }
          }
        ]
      },
      // {
      //   test: /\.css$/,
      //   include: path.join(__dirname, 'src/components'),
      //   loader: 'css-loader',
      //   //use: ['style-loader', 'css-loader']
      //   // use: [
      //   //   'style-loader',
      //   //   {
      //   //     loader: 'typings-for-css-modules-loader',
      //   //     options: {
      //   //       modules: true,
      //   //       namedExport: true
      //   //     },
      //   //   },
      //   //   'css-loader',
      //   // ],
      // },
    ]
  },

Edit

I've found out that webpack serve must be restarted for these sorts of changes to take affect (even with reloading turned on). The new issue is: although Webpack successfully builds, but any import of the classes in the .ts files returns an undefined. So none of the styles are applied. I've update the title to reflect the new issue.

What's strange is that my language server (tsserver) can auto complete the classes defined in the .css file from import * as styles from './styles.module.css', but if I console.log() it before I return my TSX, I always see an undefined value. I've confirmed that the mangled CSS modules names are in the final rendered page by finding the inline styles in the browser inspector. I've also tried declaring a styles.module.css.d.ts file (even though I shouldn't need to due to the Typescript plugin) just in case, but that still did not fix the issue. I've also tried (from this answer) to rename from styles.module.css to <component name here>.module.css but that also did not work.

Does anyone know what could be causing this?


Solution

  • import * as styles from "module-path"; is semantically different to import styles from "module-path";. Basically don't assume these non-javascript made-up modules follow any advanced module rules and always default import them.

    EDIT: This answer is sort of outdated, in that the default way as of version 7.0.0 of css-loader is actually module import, i.e. import * as styles from "module-path"; and in that case styles.default would refer to .default class declaration within the css module file, not the default export of that css-but-actually-js-module. If changing from module import to default import fixes your problem after 2024-04-04, that means you have a dependency problem.