How to edit webpack to use SCSS in React app? - css

I created a new React app with create-react-app (using React ver. 16.6.3). Now I want to use SCSS for my components. So first I ran the eject script. Then in webpack.config.dev.js I did the following edit:
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}),
}
I also installed node-sass package.
Then I created my Test .scss file:
.Test {
background-color: gold;
.Header {
color: lighten(purple, 20%);
}
}
And my Test component with importing the .scss file
import React from 'react';
import style from './test.scss';
const Test = (props) => (
<div className={style.Test}>
This is div1
<div className={style.Header}>Div 2</div>
</div>
);
export default Test;
That didn't work and I didn't see any styling. I tried to import the .scss directly and use it:
import './test.scss';
...
<div className='Test'>
This is div1
<div className={style.Header}>Div 2</div>
</div>
...
That did work and I saw the styling on the div with className='Test'.
I tried to change the webpack as follows:
const CSSModuleLoader = {
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
localIdentName: '[local]__[hash:base64:5]',
minimize: true
}
}
const CSSLoader = {
loader: 'css-loader',
options: {
modules: false,
sourceMap: true,
minimize: true
}
}
const postCSSLoader = {
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: () => [
autoprefixer({
browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9']
})
]
}
}
...
{
test: /\.scss$/,
exclude: /\.module\.scss$/,
use: ['style-loader', CSSLoader, postCSSLoader, 'sass-loader']
},
{
test: /\.module\.scss$/,
use: [
'style-loader',
CSSModuleLoader,
postCSSLoader,
'sass-loader',
]
},
At first I got an autoprefixer not defined error. I imported it with const autoprefixer = require('style-loader') - that made this error disappear, though I'm still not convinced that was the correct require and correct fix.
But then I got the following error:
Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[2].oneOf[0].use should be one of these:
non-empty string | function | object { loader?, options?, ident?, query? } | function | [non-empty string | function | object { loader?, options?, ident?, query? }]
-> Modifiers applied to the module when rule is matched
Details:
* configuration.module.rules[2].oneOf[0].use should be a string.
* configuration.module.rules[2].oneOf[0].use should be an instance of function
* configuration.module.rules[2].oneOf[0].use should be an object.
* configuration.module.rules[2].oneOf[0].use should be an instance of function
* configuration.module.rules[2].oneOf[0].use[1] should be a string.
* configuration.module.rules[2].oneOf[0].use[1] should be an instance of function
* configuration.module.rules[2].oneOf[0].use[1] has an unknown property 'loaders'. These properties are valid:
object { loader?, options?, ident?, query? }
Don't know how to deal with that...
How do I configure webpack to either immediately compile .scss to .css in the same directory (that way I can import .css and use it regularly with style.Class) or to use the .scss import in my file in the same manner and later compile it to .css for production?

Please use below rules in webpack file and remove extra code.
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]

Related

Svelte: Is there a way to make global css variables in scope of svelte components?

I have set my global.css file which I import in index.js
--root {
--main-color: red;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
index.js
import "./global.css";
import App from "./App.svelte";
const app = new App({
target: document.body
});
My webpack setup
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: {
loader: "svelte-loader",
options: {
emitCss: true,
hotReload: true
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: "style-loader", options: { sourceMap: true } },
use: [
{ loader: "css-loader", options: { sourceMap: true } },
{
loader: "postcss-loader",
options: {
sourceMap: true,
ident: "postcss",
plugins: loader => [
require("postcss-import")({}),
require("postcss-preset-env")(),
require("cssnano")()
]
}
}
]
})
}
]
},
plugins: [new HtmlWebpackPlugin(), new ExtractTextPlugin("styles.css")]
};
Works perfect for setting up global css for the entire app. But I am trying to use the --main-color in my svelte components. Is there a way to inject them down to all the components' css ?
Since I import global.css first, it should work as it emits a file with --root{} first then rest of the component styles.
You can place global styles under /routes/index.svelte file, like the example below:
<style>
:global(:root){
--header-color: purple
}
</style>
And simply use it anywhere like normally how you use CSS variables like so:
h1 {
color: var(--header-color);
}
I was busy with this, trying different webpack settings etc., seeing that the output css should work, I just could not find why it did not work. I wrote the post before trying for one last time, which wasted another hour. I finally found the error.
Instead of using :root{} I have mistyped it --root{}. I have posted it anyways, in case someone is stuck with the same mistake.

Css is not used by jsx in ReactJS

I use ReactJS with Webpack and TypeScript.
I'm trying to include CSS with React. But my CSS is not use by my React application.
I created a global.d.ts file to declare my css module. I imported my css into my.tsx file but when I add a class to an element, nothing works.
I have already tried to install a css module but I didn't succeed. So, I just made the solution to create a global.d.ts file
global.d.ts :
declare module '*.css';
Hello.tsx
import * as css from './Hello.css';
export interface HelloProps {
compiler: string;
framework: string;
}
export class Hello extends React.Component<HelloProps, {}> {
render() {
return (
<form>
<p className={css.test}> Test </p>
</form>
);
}
}
Hello.css
.test {
color: red;
}
webpack.config.js
module: {
rules: [
.....
{ test: /\.css$/, exclude: /node_modules/, use: ['style-loader', 'css-loader'] }
]
},
The word "test" is written in black.
When it should be in red
This would be due to you not having modules set to true in your css-loader options.
// webpack.config.js
rules: [
{
test: /.css$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
],
Also, i would just do the following as there's no need for the * import.
import css from './Hello.css'
and then in your jsx:
<Component className={css.test} />
im quite new to react but i think if you remove:
"import * as css from './Hello.css';" And Replace it with "import './Hello.css';"

Webpack 4 - Style-loader/url not working

I'm having my webpack set up and it's running all fine, but in development it is serving my compiled scss stylesheets inline instead of using an URL.
module: {
rules: [
{
test: /\.scss$/,
use: [
{ loader: "style-loader"},
{ loader: "css-loader" },
{ loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')]
}
},
{ loader: "sass-loader" }
]
}
]
}
So I grabbed the docs and read up on how to use a single CSS file instead. I updated my webpack config to the following and since all loaders are running in reverse order this should be working;
module: {
rules: [
{
test: /\.scss$/,
use: [
{ loader: "style-loader/url"},
{ loader: "file-loader" },
{ loader: "css-loader" },
{ loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')]
}
},
{ loader: "sass-loader" }
]
}
]
}
It results in no errors, and inserts the following stylesheet into my header;
<link rel="stylesheet" type="text/css" href="6bbafb3b6c677b38556511efc7391506.scss">
As you can see it's creating an scss file, whereas I was expecting a .css file. I tried moving the file-loader around but that didn't work either and resulted in several crashes. Any idea how to turn this into a working css file?
I can't use mini-css-extract in my dev env since I'm using HMR. I already got this working on my prod env.
Update: When removing css-loader it compiles and shows my css applied to the page. But when I inspect the elements everything is on line 1 and the file it refers to can not be found
I'm importing my css like this in index.js by the way;
import '../css/styles.scss';
You can install extract-text-webpack-plugin for webpack 4 using:
npm i -D extract-text-webpack-plugin#next
The you can define the following constants:
// Configuring PostCSS loader
const postcssLoader = {
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
// Write future-proof CSS and forget old preprocessor specific syntax.
// It transforms CSS specs into more compatible CSS so you don’t need to wait for browser support.
require('postcss-preset-env')()
]
}
};
// Configuring CSS loader
const cssloader = {
loader: 'css-loader',
options: {
importLoaders: 1
}
};
Then in your SASS loader section, you can use the following:
ExtractTextPlugin.extract({
use: [cssloader, postcssLoader, 'sass-loader']
})
Then in you plugins section, you need to use the following:
new ExtractTextPlugin({
filename: 'css/[name].css'
)
Now suppose that your entry section is like below:
entry: {
app: 'index.js'
}
The generated CSS will be named as app.css and placed inside the css folder.
Another useful plugins for handling these type of post creating operations are:
HtmlWebpackPlugin and HtmlWebpackIncludeAssetsPlugin
Working with these plugins along with extract-text-webpack-plugin gives you a lot of flexibility.
I had a similar issue with webpack, after searching for a long time i found the soluton of combining a few plugins:
This is my result config: (as a bonus it preserves your sass sourcemaps;))
watch: true,
mode: 'development',
devtool: 'source-map',
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css", //make sure you use this format to prevent .scss extention in the hot reload file
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.scss$/,
use: [
'css-hot-loader', //5. this will hot load all the extracted css.
MiniCssExtractPlugin.loader, //4 this will extract all css
{
loader: "css-loader", //3. this is where the fun starts
options: {
sourceMap: true
}
},
{
loader: "postcss-loader", //2. add post css
options: {
sourceMap: true
}
},
{
loader: "sass-loader", //1 . you can ingore the globImporter
options: {
importer: globImporter(),
includePaths: ["node_modules"],
sourceMap: true
}
}
]
},
]
}

Webpack can't fix CSS override issue and bundle <style> elements in <head>

In my app, lets say I have two JS pages A and B, and each import a different stylesheet (import '../style/<A or B.css>').
Both stylesheets have identical classnames but but different properties.
I run yarn run dev ==> dev: webpack-dev-server --inline --hot ==> webpack -p
This is what my html <head> looks like
https://imgur.com/a/1JVb5
page A stylesheet is loaded first, then page B css style is loaded after
When I go to Page B, the css is correct
When I go to Page A, the css is mixed up and some class styles are overriden by page B.css.
My project structure is like
public/
bundle.js
index.html
src/
components/
pages/
style/
App.js
index.js
package.json
webpack.config.js
my webpack.config.js is
const path = require('path');
var config = {
entry: path.resolve(__dirname, 'src', 'index.js'),
output: {
path: path.resolve(__dirname, 'public'),
filename: 'bundle.js'
},
devServer: {
contentBase: path.resolve(__dirname, 'src'),
publicPath: path.resolve(__dirname, 'public')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: [
{ loader: 'babel-loader',
options: { presets: ['react','env'] } }
]
},
{
test: /\.css$/,
use: [
{ loader: "style-loader?singleton",
options:
{ singleton: true }
},
{ loader: "css-loader" }
]
}
]
}
};
module.exports = config;
I want Webpack to merge the multiple elements and fix the css override issue
In Webpack, I have tried style-loader?singleton and { singleton: true } but it didnt work.
EDIT 1: looking into extract-text-webpack-plugin
EDIT 2:
import movieStyle from '../style/MovieDetail.css'
...
return (
<div id="CellDetail_right" className={ movieStyle['cell-detail-right'] }>...</div>
)
Ok, I added options: { modules: true } and it didnt work. My classNames are hyphenated and after compiling the browser renders the components WITHOUT any style or classes.
Div on browser looks like <div id="CellDetail_right">...<div>
One solution is to enable local scoped css to avoid styles bleeding/overrides.
Update your css-loader options to include modules: true
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: { singleton: true }
},
{
loader: "css-loader",
options: {
modules: true,
camelCase: 'dashes',
localIdentName: '[path][name]__[local]'
}
}
]
}
Then, using in your components as:
import styles from '../style/MovieDetail.css';
function MyComponent() {
return (
<div className={styles.container}>
<div className={styles.cellDetailRight}>Some Content</div>
</div>
);
}
This ensures that despite you have more .container rules defined in other css files, this particular rule becomes to something like ._-path-to-component__container.
Using camelCase: 'dashes' in options transform your hyphenated rules.
dashes in class names will be camelized
You can also review my webpack-demo project which includes configs to handle your scenario.
Check the webpack configurations
Read more on css-loader options

How to load css in react app?

I created a react app using create-react-app and everything works fine except that I can't figure out how to load css/stylus files.
In my previous react project that wasn't created using create-react-app I used webpack.config but now I don't know where to include this file and how to use it.
This is my folder structure:
.
+--client
| +--node_modules
| +--public
| +--src
| +--package.json
+--server
| +--node_modules
| +--src
| +--.babelrc
| +--package.json
This is my previous webpack.config.dev.js file:
import path from 'path';
import webpack from 'webpack';
export default {
entry: [
'webpack-hot-middleware/client?reload=true',
path.join(__dirname, '/client/index.js')
],
output: {
filename: 'bundle.js',
path: '/',
publicPath: '/'
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.js$/,
include: path.join(__dirname, 'client'),
loader: 'babel-loader',
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
{
loader: 'stylus-loader',
},
],
},
{
test: /\.css?$/,
use: [
'style-loader',
{
loader: 'css-loader',
},
],
},
],
},
resolve: {
extensions: [ '.js', '.styl' ]
}
}
Please let me know if I need to provide additional information.
There is detailed informations about how to add SCSS and Less files to create-react-app project on their documentation. You can check here for more detail.
You can add basic CSS styling with just importing the file like shown below;
import '/css/main.css';
Or you can add as classNames
const styles = {
header: {
fontSize: 18,
color: '#909090',
}
};
export default class Header extends React.Component {
render() {
return (
<h1 className={styles.header}>This is a header</h1>
);
}
}
One of the approaches involves loading CSS files (via css-loader) from your JS files, for a Text.jsx
import React from 'react';
import './screen.css'; // if screen.css is located next to Text.jsx
class Text extends React.Component {
// ...
}
export default Text;
You could also generate an output CSS, using e.g. ExtractTextPlugin (see Webpack2 loading and extracting LESS file)

Resources