Styles not working in isomorphic React implementation using webpack - reactjs

Server rendered html does not contain any css. Only when react runs on client side, the css gets applied. What am i doing wrong?
Webpack config server + client: (shortened for brevity)
module.exports = [
{
name: "simple-server",
entry: [path.join(__dirname, "..", "src", "server.js")],
output: {
path: path.join(__dirname, "..", "build"),
filename: "server.js",
libraryTarget: "commonjs2",
},
target: "node",
externals: [nodeExternals()],
module: {
loaders: [
{
test: /\.scss$/,
loaders: ["css", "sass"],
},
{
test: /\.css$/,
loader: ["css"],
}
],
},
resolve: {
extensions: ["", ".js", ".jsx", ".scss"],
modulesDirectories: [ "src", "node_modules"],
},
},
{
name: "simple-client",
context: path.join(__dirname, "..", "src"),
entry: {
app: ["handlers/App", "webpack-hot-middleware/client?path=__webpack_hmr"],
},
devtool: "cheap-module-inline-source-map",
output: {
path: path.join(__dirname, "..", "build", "assets"),
filename: "[name].js",
publicPath: "assets/",
},
target: "web",
module: {
loaders: [
{
test: /\.scss$/,
loaders: ["style-loader","css-loader?sourceMap","postcss-loader","sass-loader?sourceMap"],
},
{
test: /\.css$/,
loaders: ["style-loader","css-loader?sourceMap","postcss-loader"],
}
],
},
postcss: () => [autoprefixer({ browsers: ["last 5 versions", "> 5%"] })],
resolve: {
extensions: ["", ".js", ".jsx", ".scss"],
modulesDirectories: [
"src",
"node_modules",
],
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new HtmlWebpackPlugin({
filename: "app.html",
template: "templates/main.html",
})
new webpack.HotModuleReplacementPlugin(),
],
},
];
Sample component :
import React from "react";
import "Styles.scss";
export default Component extends .... {}

You can tell webpack to create separate bundle for css when it creates js bundle. And use them in html head
<link rel="stylesheet" type="text/css" href="/static/style.css">
(There are many articles which suggest that it is better to have css in the head of html).
How to instruct webpack to separate css:
https://webpack.github.io/docs/stylesheets.html#separate-css-bundle
In short:
You have to install :
npm install extract-text-webpack-plugin --save
Add this plugin to plugin list:
plugins: [
new ExtractTextPlugin("style.css")
]
And in loaders:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
{
test: /\.sass$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")
}
Im not sure if this works correctly with hmr (reloading css) because when I work with hmr Im not need server side rendering.
I have two webpack configs : dev and production. dev config use hmr and doesn't separate css (one bundle) and doesn't render server side. And production doesn't use hmr, have separated css and js and server side rendering.
You could check my configuration:
https://github.com/uhlryk/my-express-react-seed

Related

Multiple Webpack in React for SSR

I have a React app that I wrote with Webpack (without using CRA), and I am trying to implement server-side rendering to it. I wrote two webpack config files (one for server and one for client), in my original webpack config I had loaders for most of the types im going to use (ts, js, css, image formats and ttf), so my new config files had the same loaders but each one had a different output path (server under dist and client under dist/public).
However, with this I was having the same font in both output path, so I removed from one of them and then from both config files, the font loader, and still I was getting the fonts on both directories. Below are my webpack configurations.
To note that I downloaded a font and placed it under my src folder and using it in my index.css which is imported in App.tsx (which is used in both client.tsx and server.tsx).
webpack.config.server.ts:
import path from 'path';
import webpack from 'webpack';
import nodeExternals from 'webpack-node-externals';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const config: webpack.Configuration = {
mode: 'development',
entry: path.resolve(__dirname, '..', 'src', 'server.tsx'),
output: {
path: path.join(__dirname, '..', 'dist'),
filename: 'server.js',
clean: true
},
target: 'node',
externals: [ nodeExternals() ],
resolve: {
extensions: [ '.ts', '.tsx', '.js', '.jsx', '.json' ]
},
module: {
rules: [
{
test: /\.jsx?$/i,
exclude: /node_modules/i,
use: 'babel-loader'
},
{
test: /\.tsx?$/i,
exclude: /node_modules/i,
use: 'ts-loader'
},
{
test: /\.css$/,
use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}/*,
{
test: /\.ttf$/i,
type: 'asset/resource'
}*/
]
},
plugins: [
new MiniCssExtractPlugin()
]
};
export default config;
webpack.config.client.ts:
import path from 'path';
import webpack from 'webpack';
const config: webpack.Configuration = {
mode: 'development',
entry: path.resolve(__dirname, '..', 'src', 'client.tsx'),
output: {
path: path.join(__dirname, '..', 'dist', 'public'),
filename: 'bundle.js',
clean: true
},
target: 'web',
resolve: {
extensions: [ '.ts', '.tsx', '.js', '.jsx', '.json' ]
},
module: {
rules: [
{
test: /\.jsx?$/i,
exclude: /node_modules/i,
use: 'babel-loader'
},
{
test: /\.tsx?$/i,
exclude: /node_modules/i,
use: 'ts-loader'
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}/*,
{
test: /\.ttf$/i,
type: 'asset/resource'
}*/
]
}
};
export default config;
This is the build directory (with font commented and uncommented).
I find it a bit redundant for both configs to generate the same font, is there something missing in my configuration I should add? I am still trying to figure out how to properly implement server-side rendering so my configuration files may not be the best configurations.

How to configure css modules in my React app

Im not using the create react app project. Here is my webpack.base.js (someone else set this up so im just working off what i have)
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: "./src/index.tsx",
target: "web",
mode: "development",
output: {
filename: "[name].[chunkhash].bundle.js",
path: path.resolve(__dirname, "/opt/valkyrie/html"),
clean: true
},
watchOptions: {
ignored: '**/node_modules',
},
resolve: {
modules: ["src", "node_modules"],
extensions: [
".tsx",
".ts",
".js",
".jsx",
".svg",
".css",
".json",
".mdx",
".png",
".scss",
".sass"
],
alias: {
react: path.resolve('./node_modules/react')
}
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '/public/icons/[name].[ext]'
}
},
],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Spectrum App",
template: __dirname + "/public/index.html",
inject: "body",
filename: "index.html",
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css",
}),
]
};
Can someone tell me how I turn on css modules? Googling around i see a number of posts about turning it on for the create react app but they mention existing settings that i dont have in mine.
Im using React 17.0.2
Add this to webpack module
module: {
loaders: [
{
test: /\.css/,
loaders: ['style', 'css'],
include: `${__dirname}/src`,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
and please rename the css files as component_name.module.css and import the css file in jsx/js as import styles from './component_name.module.css';

Using Reactjs with electronjs getting unexpected token '<' error

I've been trying to run electron js with a react view layer. Both have different webpack configs. While the electron seems to be working fine, the react portion throws error.
Webpack config
let rendererConfig = {
entry: {
index: path.join(__dirname, '../src/render/index.js')
},
module: {
rules: [
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/,
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'BSK',
filename: 'index.html',
chunks: ['index'],
template: path.resolve(__dirname, '../src/render/index.html'),
nodeModules: devMode
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron'),
globalObject: 'this'
},
target: 'electron-renderer',
}
This is my .babelrc
{
"env": {
"main": {
"presets": ["#babel/preset-env"]
},
"renderer": {
"presets": [
"#babel/preset-env",
"#babel/preset-react"
],
"plugins": ["react-hot-loader/babel","#babel/plugin-proposal-class-properties","emotion"]
}
},
"plugins": [
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-runtime"
]
}
I have all the required dev dependencies, but still the babel loader seems to be ignoring the presets
Adding '/' to publicPath of output fixed the issue
reference

toastr not showing when imported in a jsx file

I have a react app that uses webpack to bundle JS and CSS into 1 file and output it into a destination folder. I've recently added toastr to 1 of my jsx file:
import toastr from "toastr";
import "toastr/build/toastr.min.css"
Running the app and viewing the source, i've verified in the browser (viewing the source files) that toastr.min.js is included in the JS bundle and toastr.min.css is included in the CSS bundle. However, the toastr notification doesn't show. There is no error and a scrollbar appears in the right-side for a few seconds so I suspected the toastr code is working, just that the CSS is not properly styling for some reason.
I removed this line:
import "toastr/build/toastr.min.css"
and then directly added this to html
<link rel="stylesheet" type="text/css" href="~/css/toastr.min.css" />
and now it works. But I want to make it work where toastr.min.css is included in the bundle. Is there anything I'm missing?
webpack config
const path = require("path");
const webpack = require("webpack");
const miniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
home: "./Scripts/Components/Home/main.js",
login: "./Scripts/Components/Login/main.js",
vendor: [
"jquery",
"react",
"react-dom",
"react-router-dom",
"react-css-modules",
]
},
mode: "development",
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: "all",
name: "vendor",
test: "vendor",
enforce: true
}
}
}
},
output: {
publicPath: "/js/",
path: path.join(__dirname, "/wwwroot/js/"),
filename: "[name].bundle.js"
},
devtool: "source-map",
plugins: [
new miniCssExtractPlugin({
filename: "../css/[name].css"
}),
],
module: {
rules: [{
test: /\.jsx$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["env", "react"]
}
}
}, {
test: /\.css$/,
use: [{
loader: miniCssExtractPlugin.loader,
}, {
loader: "css-loader",
query: {
modules: true,
localIdentName: "[name]__[local]___[hash:base64:5]"
}
}]
}]
}
};

WebPack loads all semantic-ui components

I'm currently working on a project and i need to configure WebPack. In the project, we are also using ReactJS and Semantic-UI. Here is webpack.config.js :
var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
context: __dirname,
entry: {
react: ["react", "react-dom"],
home: './assets/js/index.jsx',
},
output: {
path: path.resolve('./assets/bundles/'),
filename: "[name].js",
},
plugins: [
new BundleTracker({filename: './webpack-stats.json'}),
new webpack.optimize.CommonsChunkPlugin({
names: ["react"],
}),
new webpack.optimize.CommonsChunkPlugin({
name: "home",
chunks: ['home'],
filename: "[name]-[hash].js",
}),
new BundleAnalyzer(),
],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: { presets: ["es2015", "react"] }
},
],
},
resolve: {
modules: ['node_modules', 'bower_components'],
extensions: ['*', '.js', '.jsx']
},
};
In assets/js/index.jsx file, we just have these import statements :
import React from "react";
import ReactDOM from 'react-dom';
import { Button } from 'semantic-ui-react';
By running webpack command, it outputs two files:
react.js: 779 KB
home-[some_hash_number].js: 1.5 MB
Using webpack-bundle-analyzer plugin, we get this:
As you see the red rectangle in the picture, all of the semantic-ui react components are bundled into home.js file although i just imported Button component from in assets/js/index.js file and that's why the output file gets too big.
Is there any way to just bundle needed components?
UPDATE
Reading #Alexander Fedyashov answer, i installed babel-plugin-lodash and updated webpack.config.js accordingly:
var path = require("path");
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
var BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
context: __dirname,
entry: {
react: ["react", "react-dom"],
home: './assets/js/index.jsx',
},
output: {
path: path.resolve('./assets/bundles/'),
filename: "[name].js",
},
plugins: [
new BundleTracker({filename: './webpack-stats.json'}),
new webpack.optimize.CommonsChunkPlugin({
name: "react",
}),
new webpack.optimize.CommonsChunkPlugin({
name: "home",
chunks: ['home'],
filename: "[name]-[hash].js",
}),
new BundleAnalyzer(),
],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: ["lodash", { "id": ["lodash", "semantic-ui-react"] }],
presets: ["es2015", "react"],
}
},
],
},
resolve: {
modules: ['node_modules', 'bower_components'],
extensions: ['*', '.js', '.jsx']
},
};
Now everything is working and only needed components are loaded.
It should be splitted by Webpack, but in fact tree shaking doesn't work. You can use babel-plugin-lodash as described in SUIR docs.
You should keep in mind, that some of SUIR's components are dependent from each other, i.e.:
Button requires Icon and Label
Label requires Icon and Image
Image requires Dimmer
Dimmer requires Portal.
However, plugin will allow to strip such components as Rail, Reveal and Advertisement if you don't use them.
There is a new feature on Webpack 2 to solve this issue, read this article
https://medium.com/#adamrackis/vendor-and-code-splitting-in-webpack-2-6376358f1923

Resources