Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use CSS Modules in React components with Typescript built by webpack

I want to use the css-loader with the 'modules' option of webpack in a React application written in Typescript. This example was my starting point (they are using Babel, webpack and React).

webpack config

var webpack=require('webpack');
var path=require('path');
var ExtractTextPlugin=require("extract-text-webpack-plugin");

module.exports={
    entry: ['./src/main.tsx'],
    output: {
        path: path.resolve(__dirname, "target"),
        publicPath: "/assets/",
        filename: 'bundle.js'
    },
    debug: true,
    devtool: 'eval-source-map',
    plugins: [
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.UglifyJsPlugin({minimize: true})
    ],
    resolve: {
        extensions: ['', '.jsx', '.ts', '.js', '.tsx', '.css', '.less']
    },
    module: {
        loaders: [
            {
                test: /\.ts$/,
                loader: 'ts-loader'
            },
            {
                test: /\.tsx$/,
                loader: 'react-hot!ts-loader'
            }, {
                test: /\.jsx$/,
                exclude: /(node_modules|bower_components)/,
                loader: "react-hot!babel-loader"
            },
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: "babel-loader"
            }, {
                test: /\.css/,
                exclude: /(node_modules|bower_components)/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader')
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin("styles.css", {allChunks: true})
    ],
    postcss: function() {
        return [require("postcss-cssnext")()]
    }
}

This is a React component I want to style with an accompanying CSS file:

import React = require('react');
import styles = require('../../../css/tree.css')

class Tree extends React.Component<{}, TreeState> {
...

    render() {
        var components = this.state.components
        return (
            <div>
                <h3 className={styles.h3} >Components</h3>
                <div id="tree" className="list-group">
                    ...
                </div>
            </div>
        )
    }
}

    export = Tree

tree.css

.h3{
    color: red;
}

No matter what I'm doing (tried changing the import syntax, tried declaring the 'require' for ts-loader, described here, I always get:

Uncaught Error: Cannot find module "../../../css/tree.css"

at runtime and

error TS2307: Cannot find module '../../../css/tree.css'.

by the TS compiler. Whats happening? Seems to me that css-loader is not even emitting ICSS? Or is it ts-loader behaving wrong?

like image 714
Shady Avatar asked Jan 26 '16 12:01

Shady


People also ask

Can webpack process CSS files?

To be able to use CSS in your webpack app, you need to set up a new loader. Out-of-the-box, webpack only understands Javascript and JSON. With a loader, you can translate another type of file to a format that webpack understands and can work with. There are many webpack loaders and each loader has a specific purpose.


4 Answers

import has special meaning to TypeScript. It means that TypeScript will attempt to load and understand the thing being imported. The right way is to define require like you mentioned but then var instead of import:

var styles = require('../../../css/tree.css')`
like image 123
James Brantly Avatar answered Sep 21 '22 01:09

James Brantly


  1. Declare 'require' as per ts-loader documentation.
  2. Use 'require' as generic with < any > type: require< any >("../../../css/tree.css").

*.d.ts file

declare var require: {
   <T>(path: string): T;
   (paths: string[], callback: (...modules: any[]) => void): void;
   ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void;
}; 

*.tsx file with component

const styles = require<any>("../../../css/tree.css");
...
<h3 className={styles.h3}>Components</h3>

I know it was already answered, but I was struggling with it for a while before I realized I need to use generic type specification, without that I wasn't able to access content of CSS file. (I was getting error: Property 'h3' does not exists on type '{}'.)

like image 42
Martin Avatar answered Sep 21 '22 01:09

Martin


I had similar problem. For me, works import:

import '../../../css/tree.css';

Webpack change this like any other normal imports. It change it to

__webpack_require__(id)

One drawback is that you lost control on style variable.

like image 45
Krzysztof Sztompka Avatar answered Sep 23 '22 01:09

Krzysztof Sztompka


You can use https://github.com/Quramy/typed-css-modules, which creates .d.ts files from CSS Modules .css files. Please see also https://github.com/css-modules/css-modules/issues/61#issuecomment-220684795

like image 23
Yosuke Kurami Avatar answered Sep 23 '22 01:09

Yosuke Kurami