Alp
Alp

Reputation: 29739

React: Inline CSS modules in JSX with Webpack

I have a minimal React component which consists of two files: button.jsx and button.less. The styles are imported and the class names are appended with a hash to make all styles local to the component.

This is great, but i'd like to have all component code in one file. Is it possible to inline the styles in jsx file without losing css modularity?

Current Code

button.jsx

import React from 'react';
import styles from './button.less'

export default class Button extends React.Component {
    render() {
        return <button className={styles.primary}>{this.props.text}</button>;
    }
}

button.less

@import '~semantic-ui/src/definitions/elements/button.less';

.common {
    composes: ui button;
}

.primary {
    composes: common primary;
}

webpack.config.js (relevant bits)

module: {
    loaders: [
        {
            test: /\.jsx$/,
            loader: 'babel'
        },

        {
            test: /\.less$/,
            loader: "style!css?modules&importLoaders=1!less"
        }
    ]
},

What i'd like to write instead

button.jsx

<style lang="less" modules>
    @import '~semantic-ui/src/definitions/elements/button.less';

    .common {
        composes: ui button;
    }

    .primary {
        composes: common primary;
    }
</style>

import React from 'react';

export default class Button extends React.Component {
    render() {
        return <button className={styles.primary}>{this.props.text}</button>;
    }
}

Inspired by vue.js and vue-loader.

I believe this is a duplicate of this unanswered question: Using css-loader inline with Webpack + React

Upvotes: 11

Views: 3184

Answers (2)

Christopher Davies
Christopher Davies

Reputation: 4551

I wrote a Webpack loader for this very purpose:

https://github.com/chrisdavies/stylextract-loader

It allows you to write a single style tag per JSX file and it supports webpack CSS modules, too, if you want.

At build time, it extracts the rules from your style tag and moves them out to an external CSS file.

I should note that because it simply extracts your rules out to an external CSS file, it plays nice with SASS, autoprefixer, etc

Upvotes: 3

Kreozot
Kreozot

Reputation: 1576

You can use callback-loader for this. This is actualy a workaround, but it do the trick. Just implement a callback which will extract your css-code and replace it with appropriate import. For example:

webpack.config.js

var fs = require('fs');
var cssIndex = 0;
// Do not forget to create and clean temporary folder "cssTemp" before
var webpackConfig = {
    ...
    resolve: {
        alias: {
            cssTemp: path.resolve('./cssTemp')
        }
    },
    module: {
        loaders: [
            { test: /\.jsx$/, loader: "callback!babel" }
        ]
    },
    callbackLoader: {
        cssCallback: function(code) {
            var filename = cssIndex + '.less';
            cssIndex++;
            // Save the css code from the callback argument
            fs.writeFileSync('./cssTemp/' + filename, code);
            // Return the import statement which will replace the callback statement
            return 'import styles from "cssTemp/' + filename + '";';
        }
    }
    ...
};

button.jsx

import React from 'react';
cssCallback(`
    @import '~semantic-ui/src/definitions/elements/button.less';

    .common {
        composes: ui button;
    }

    .primary {
        composes: common primary;
    }
`);

export default class Button extends React.Component {
    render() {
        return <button className={styles.primary}>{this.props.text}</button>;
    }
}

Upvotes: 2

Related Questions