Now in the year 2021, all you need to do is add a src/Globals.d.ts to your project with these lines:
declare module "*.module.css";
declare module "*.module.scss";
// and so on for whatever flavor of css you're using
Then install and add
{
"compilerOptions": {
"plugins": [{ "name": "typescript-plugin-css-modules" }]
}
}
to your tsconfig.
Example of this correctly functioning in VS code after making that simple change (root is a class defined in my stylesheet):

Webpack and tsc also compile correctly on the command line.
Answer from Matt Wonlaw on Stack Overflow
» npm install typescript-plugin-css-modules
reactjs - How to import CSS modules with Typescript, React and Webpack - Stack Overflow
TIL there is a typescript plugin for css modules
What i am doing wrong?
reactjs - How can I import CSS Modules in a TypeScript file? - Stack Overflow
Now in the year 2021, all you need to do is add a src/Globals.d.ts to your project with these lines:
declare module "*.module.css";
declare module "*.module.scss";
// and so on for whatever flavor of css you're using
Then install and add
{
"compilerOptions": {
"plugins": [{ "name": "typescript-plugin-css-modules" }]
}
}
to your tsconfig.
Example of this correctly functioning in VS code after making that simple change (root is a class defined in my stylesheet):

Webpack and tsc also compile correctly on the command line.
A) As you are saying, there is one simplest (not best) option to use require:
const css = require('./component.css')
- We need to have typings for
requireas it's not standard feature in typescript. Simplest typing for this specific require may be:
declare function require(name: string): string;Webpack will then compile typescript and use modules properly - BUT without any IDE help and class names checks for build.
B) There is better solution to use standard import:
import * as css from './component.css'
- enables full class names IntelliSense
- requires types definition for each css file, otherwise
tsccompiler will fail
For proper IntelliSense, Webpack needs to generate types definition for each css file:
Use webpack typings-for-css-modules-loader
webpackConfig.module.loaders: [ { test: /\.css$/, loader: 'typings-for-css-modules?modules' } { test: /\.scss$/, loader: 'typings-for-css-modules?modules&sass' } ];Loader will generate
*.css.d.tsfiles for each of css files in your codebase- Typescript compiler will understand that css import will be module with properties (class names) of type string.
Mentioned typings-for-css-loader contains a bug and because of types file generation delay, it's best to declare global *.css type in case our *.css.d.ts file is not generated yet.
That little bug scenario:
- Create css file
component.css - Include it in component
import * as css from './component.css' - Run
webpack - Typescript compiler will try to compile code (ERROR)
- Loader will generate Css modules typings file (
component.css.d.ts), but it's late for typescript compiler to find new typings file - Run
webpackagain will fix build error.
Easy fix is to create global definition (eg. file called typings.d.ts in your source root) for importing CSS Modules:
declare module '*.css' {
interface IClassNames {
[className: string]: string
}
const classNames: IClassNames;
export = classNames;
}
This definition will be used if there is no css file generated (eg. you have added new css file). Otherwise will be used generated specific (needs to be in same folder and named same as source file + .d.ts extension), eg. component.css.d.ts definition and IntelliSense will work perfectly.
Example of component.css.d.ts:
export const wrapper: string;
export const button: string;
export const link: string;
And if you don't want to see generated css typings you may setup filter in IDE to hide all files with extension .css.d.ts in your sources.
I just found this typescript plugin for css modules to make using classNames inside of components more typesafe (also gives intellisense)! https://www.npmjs.com/package/typescript-plugin-css-modules
with the plugin:
image showing usage of css modules with a typescript warning for the wrong class namewithout the plugin there is no warning when you rename your css class:
image showing usage of css modules with no warningsimport * 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.
npm install --save-dev style-loader css-loader css-modules-dts-loader
and
const isNamedExport = true;
// webpack.config.js
module.exports = {
// ... other webpack config settings
module: {
rules: [
{
test: /\.module\.(css|postcss|pcss|scss|sass|less|styl|sss)$/i,
use: [
"style-loader",
{
loader: "css-modules-dts-loader",
options: {
// Convert CSS class names to camelCase (default: false)
camelCase: true,
// Quote style: "single" or "double" (default: "double")
quote: "single",
// Indentation style: "tab" or "space" (default: "space")
indentStyle: "space",
// Number of spaces if indentStyle is "space" (default: 2)
indentSize: 2,
// Mode: "emit" to generate or "verify" to check the file (default: "emit")
mode: isProduction ? "verify" : "emit",
// Sort the exported class names alphabetically (default: false)
sort: true,
// Use named exports instead of interface (default: true)
namedExport: isNamedExport,
// Custom banner comment at the top of the file
banner: "// This file is automatically generated.\n// Please do not change this file!"
}
},
{
loader: "css-loader",
options: {
sourceMap: true,
modules: {
namedExport: isNamedExport,
exportLocalsConvention: "as-is",
localIdentName: "[name]__[local]___[hash:base64]"
},
importLoaders: 1
}
}
]
}
]
}
};
and import styles by the next way
if isNamedExport is true
import * as styles from "$componentName.module.css"
if isNamedExport is false
import styles from "$componentName.module.css"