How to move css-in-js (Styled Components) to an external css files during build using webpack - ReactJS - reactjs

I am trying to figure out where the CSS files are residing when I build the react project. I am using webpack and I am able to make a single CSS file for all the styles used throughout the project if I use normal CSS. When I use CSS in js using styled component, I am not getting an external CSS file.
webpack.config.js
var path = require('path');
var hwp = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: path.join(__dirname, '/src/index.js'),
output: {
filename: 'index.js',
path: path.join(__dirname, './dist'),
publicPath : '/'
},
module:{
rules:[{
exclude: /node_modules/,
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /\.css$/i,
use: [
{
loader : MiniCssExtractPlugin.loader,
options : {
publicPath: '/public/path/to/',
},
},
'css-loader'
]
}
]
},
devServer: {
historyApiFallback: true,
},
plugins:[
new hwp({template:path.join(__dirname, '/src/index.html')}),
new MiniCssExtractPlugin({
filename : '[name].css',
chunkFilename : '[id].css',
})
]
}
Contact.js
import React from 'react'
import styled from "styled-components"
const Container = styled.div`
background-color : red;
`
function contact() {
return (
<Container>
<h1>
Welcome to Contacts page
</h1>
</Container>
)
}
export default contact

This isn't supported by styled-components at the moment. From a project member -
We don't support static CSS extraction. You can try out emotion which does.
You might not need static CSS extraction actually, because of several reasons:
There's SSR which sends only critical CSS, instead of all static CSS, for the entire page. You don't even need to do SSR, but can use snapshotting (react-snapshot) or generate a static page (gatsby, et al), which basically saves a SSR result to a html.
Static extraction doesn't generate dynamic CSS, which means your page will either appear broken until the JS executes, or you'll need to defer until the JS is loaded
Caching doesn't actually buy you an advantage, because the JS bundles will likely always change in tandem with the extracted CSS
In v3 we will add preprocessing, which will actually a bigger advantage. As part of that we might support an option for static extraction, since the core library that will bring preprocessing will probably also be integrated into emotion, which does support extraction. So it might become an option. Or not 😉
Source
Emotion has also removed static css extraction
Duplicate of How can I opt for React Styled-Components to generate a physical CSS file?

Related

SCSS doesn't load on Gatsby

i try to load the SASS for my Gatsby Project but if I check the source code of the web there isn't any classes from my sass.
I am a bit confused and I followed the documentation of Gatsby.
Nothing worked so my last chance is SO.
// gatsby-config.js
plugins: [
'gatsby-plugin-react-helmet',
'gatsby-plugin-fontawesome-css',
'gatsby-plugin-sass',
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'assets',
path: `${__dirname}/static/`,
},
},
]
Here I import the style.
/**
* Add browser relation logic.
*/
require('./style/global.js');
import './style/sass/index.scss';
I followed the gatsby-plugin-sass documentation and I should be all set. After restarting the server and show source-code of my app there is now class name from my sass file.
Best Regards
Knome
I didn't integrate in any component. Because if I see the Source code
of chrome then there should be scss be loaded.
Ok, well... The SCSS is loaded as it should but the styles are not applied to any component because you've not set any class name.
Just do:
const IndexPage =()=>{
return <div className="grid-container">I'm your index page</div>
}
Like any other HTML element.

How to use app's sass variables in decorators of storybook (React)

I've built a website with Gatsby.js (static website generator with React) and am trying to add storybook to it.
I added a custom webpack config to storybook (following the instruction at https://storybook.js.org/configurations/custom-webpack-config/) to load sass and sass-resources, but can't make it work.
Followings are the configurations. I have installed all of the "style-loader", "css-loader", "sass-loader", "sass-resources-loader" as well as "node-sass" for "storybook/react" in addition to those originally added to "gatsby" and "gatsby-plugin-sass" as dependencies.
It will be great if somebody kindly advise. Thank you very much for your help in advance.
structure
root - .storybook - addon.js
- config.js
- webpack.config.js
- src - components - Component_1 - Component_1.js
- Component_1.scss
- Component_1.stories.js
- ...
- styles - _variables.scss
- mixins - _mixin_1.scs
- ...
- ...
- ...
webpack.config.js (root/.storybook/webpack.config.js)
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
loaders: [
"style-loader",
"css-loader",
"sass-loader",
{
loader: "sass-resources-loader",
options: {
resources: [
"src/styles/_variables.scss",
"src/styles/mixins/**/*.scss"
]
},
},
],
include: path.resolve(__dirname, "../")
}
]
}
};
component_1.stories.js
import React from "react";
import { storiesOf } from "#storybook/react";
import Component_1 from "./Component_1";
storiesOf("Component_1", module)
.addDecorator(story => (
<div style={{backgroundColor: $color-primary}}>
{story()}
</div>
))
.add("default", () => (
<Component_1>
This is Component 1!
</Component_1>
));
error message (shown on storybook app)
$color is not defined
ReferenceError: $grey is not defined
at http://localhost:6006/static/preview.bundle.js:80090:33
at http://localhost:6006/static/preview.bundle.js:57555:14
at http://localhost:6006/static/preview.bundle.js:57556:16
at WrapStory.render(http://localhost:6006/static/preview.bundle.js:61197:14)
at http://localhost:6006/static/preview.bundle.js:44767:21
at measureLifeCyclePerf (http://localhost:6006/static/preview.bundle.js:44047:12)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (http://localhost:6006/static/preview.bundle.js:44766:25)
at ReactCompositeComponentWrapper._renderValidatedComponent (http://localhost:6006/static/preview.bundle.js:44793:32)
at ReactCompositeComponentWrapper.performInitialMount (http://localhost:6006/static/preview.bundle.js:44333:30)
at ReactCompositeComponentWrapper.mountComponent (http://localhost:6006/static/preview.bundle.js:44229:21)
In this case, sass variable "$color-primary" is defined in "src/styles/_variables.scss". I want to use it to style the Component_1 shown in the storybook app.
I tried importing "_variables.scss" expressly to "Component_1.stories.js" and failed again (Skiped "sass-resources-loader", just used "sass-loader").
Thank you for your advices.
[edit]
In addition to above, my storybook config file is as follow.
config.js (root/.storybook/config.js)
import { configure, addDecorator } from "#storybook/react";
// automatically import all files ending in *.stories.js
const req = require.context('../src/components', true, /\.stories\.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);
There is a :export property for sharing variables. You can refer following link for sharing variables between sass and JavaScript.
https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
Try to add the path to your variables in the storybook config file
import "../src/styles/_variables.scss";

hot-module-loader apply cold to node_module package

I'm trying to figure out how I can apply cold() to just a single node package.
according to the docs I need to use setConfig and also apply another babel-loader to include just node_modules. However I can't for the life of me figure out where or how to implement this.
import { setConfig, cold } from 'react-hot-loader'
setConfig({
onComponentRegister: (type, name, file) =>
file.indexOf('node_modules') > 0 && cold(type),
})
I'm using Kendo UI React which does not support HMR. So I need to wrap the PanelBarItem so that react-hot-loader doesnt wrap the component. What I would like to do is apply this rule all my kendo packages so I don't have to explicitly call cold on each component when I use it.
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { Button } from '#progress/kendo-react-buttons';
import { PanelBar, PanelBarItem } from '#progress/kendo-react-layout';
import { changeLocale } from '../LanguageProvider/actions';
import { cold } from 'react-hot-loader';
class Home extends React.Component {
render() {
cold(PanelBarItem);
return (
<div>
<PanelBar title="Test">
<PanelBarItem title={'Sales Forecasts'}>
<PanelBarItem title={'Q1 sdfds'} />
<PanelBarItem title={'Q2 Forecast'} />
<PanelBarItem title={'Q3 Forecast'} />
<PanelBarItem title={'Q4 Forecast'} />
</PanelBarItem>
</PanelBar>
</div>
);
}
}
When you configure your webpack, you probably have something like this:
{
module: {
rules: [{
// testing for js/jsx files, and using babel-loader load them int webpack
test: /\.jsx?$/,
// Maybe looking only on your source folder
// (and looking aside from the node_modules as a consequence)
include: [
path.resolve(__dirname, '../src'),
],
use: [{
loader: 'babel-loader', // Ask babel to eat up those files and prep them for webpack
options: {
presets: [
// What ever babel presets you might use
],
plugins: [
// Some babel plugins you might use, like transform-runtime, or lodash
// This plugin is RHL's dude that goes over your code and marks
// things for patching later by the client
'react-hot-loader/babel',
]
},
}, {
// some other loaders for styles, images etc.
},]
}
// More webpack stuff
}
Usually, you would not apply the "js/jsx" loader to node_modules (as shown above) and this is why the babel plugin which RHL uses to patch things up never gets a chance to process the Kendo code.
What the documentation asks you to do it to add another loader that looks for js/jsx files but only applies the RHL babel plugin. like this:
{
module: {
rules: [{
// Your usual loaders including the usual babel-loader for your code
}, {
test: /\.jsx?$/,
include: [
// Focus on the folder of the module you want to "cold" later
// or go for all node_modules if you need to
path.resolve(__dirname, '../node_modules/<YOUR MODULE>'),
],
use: [{
loader: 'babel-loader',
options: {
plugins: [
// The only plugin
'react-hot-loader/babel',
]
},
}]
}
}
Then, when you configure RHL on the client (first thing you do) by calling setConfig you will also get the files from the now marked modules to apply cold on.
On your very very first file, just like they show in the documentation, require the rhlconfig.js file. The onComponentRegister should now also see files from node_modules.

React multiple output files of bundle of single Input file

I am working in React and want to publish my code . I am creating bundle using webpack , since I want the bundle to be divided in three parts , that is , my code should have divided in to three different files so that not only one file get populated too much . I went through the official docs of webpack and other online sites but still not found the solution for this .
Here is a complete configuration webpack.config.js that you can base yours on.
const webpack = require('webpack');
const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
const WebpackConfig = {
// multiple component entry points
entry: {
AppPart1: './src/AppPart1',
AppPart2: './src/AppPart2',
AppPart3: './src/AppPart3'
},
// generate files
output: {
path: './assets',
// creates JS files like "AppPart1.js"
filename: '[name].js'
},
module: {
preLoaders: [
// add any pre-loaders here, OPTIONAL
],
loaders: [
// add any loaders here, like for ES6+ transpiling, React JSX etc
]
},
resolve: {
extensions: ['.jsx', '.js']
},
plugins: [
// this will factor out some common code into `bundle.js`
new CommonsChunkPlugin('bundle.js'),
]
};
module.exports = WebpackConfig;
At the end of webpack build, here is what you will have in the assets folder
AppPart1.js
AppPart2.js
AppPart3.js
bundle.js
bundle.js will contain some shared code and must be included on all your pages along with the appropriate part file for the page.
If you have the three separate files, you can put multiple entry points on your webpack config:
entry: {
"bundle1":"./src/app/somefile1.jsx",
"bundle2":"./src/app/somefile2.jsx,
"bundle3":"./src/app/somefile2.jsx"
}

Trying to use css-loader with webpack to minimize our css

I've tried to go through a dozen or so different searches and read the docs on css-loader but I believe they aren't up to date.
I'm also trying to learn our react code and conventions coming from front end javascript / jquery so this is new for me.
What I want to do is take our css and minimize it using css-loader but I don't know how to do this from what I've read.
A piece of our current code looks like this - prod.config.js :
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import PurifyCSSPlugin from 'purifycss-webpack-plugin';
import baseConfig from './base.config';
const PUBLIC_PATH = '//d1yepz0pwej23y.cloudfront.net/assets/' + process.env.TRAVIS_BUILD_NUMBER + '/';
export default {
...baseConfig,
output: {...baseConfig.output, publicPath: PUBLIC_PATH },
module: {
loaders: [
...baseConfig.module.loaders, {
test: /\.(woff|woff2|eot|ttf|otf|svg)(\?v=[0-9].[0-9].[0-9])?$/,
loader: 'file?name=[sha512:hash:base64:7].[ext]',
exclude: /node_modules\/(?!font-awesome)/
}, {
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file?name=[sha512:hash:base64:7].[ext]!image?optimizationLevel=7&progressive&interlaced',
exclude: /node_modules\/(?!font-awesome)/
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss'),
exclude: /node_modules/
}
]
},
plugins: [
// extract css
new ExtractTextPlugin('[name]-[chunkhash].css'),
// set env
new webpack.DefinePlugin({
'process.env': {
BROWSER: JSON.stringify(true),
NODE_ENV: JSON.stringify('production')
}
}), ...
...baseConfig.plugins
]
};
Is this enough to give me a suggestion on how to make this work? Or suggest where i can get some more info?
The docs say that I should require the css like this:
require("css-loader?minimize!./file.css")
but I'm not sure how to implement.
Thanks!
Update:
So after trying out what #Brandon mentioned, I actually saw code in our entry already that require's the css file.
if (process.env.BROWSER) {
require('styles/app.css');
}
I updated that to:
require('css-loader?minimize!styles/app.css');
but ended up with this error:
ERROR in ./~/css-loader?minimize!./app/styles/app.css
Module build failed: CssSyntaxError: /css-loader!/Users/homeImac/workspace/node_modules/style-loader/index.js!/Users/homeImac/workspace/node_modules/css-loader/index.js?sourceMap!/Users/homeImac/workspace/node_modules/postcss-loader/index.js!/Users/homeImac/workspace/app/styles/app.css:5:1: Unknown word
but that word is #import, does this make sense? If you guys can enlighten me on why this error is appearing, I'd appreciate it.
Thanks again!
So after trying to find solutions that solve the problem with loaders, I decided to look for another solution. A plugin called Purify CSS Plugin ended up being the answer.
new PurifyCSSPlugin({
purifyOptions: { info: true, minify: true }
})
Using the loader brought on a 'unknown word' error. Everything I tried either shifted the unknown word or did nothing to change the situation. All the folks online seemed to not be able to overcome this issue either.
Just proves the fact that there is usually more than one way to fix a problem.
put that require statement in one of your app's JavaScript source files. If it is a "global" css file, then your app's main entry JS file is a good candidate.
e.g. in app.js:
require("css-loader?minimize!./file.css"); // tell webpack about your css dependency so it can load and minimize it

Resources