isomorphic-style-loader doesn't work as it supposed to - reactjs

Hey I am doing this simple react + SSR project that incorporates the isomorphic-style loader. I followed the step-by-step guide to implement it as detailed here https://www.npmjs.com/package/isomorphic-style-loader but it just doesn't work. The style I made is not showing. Can anyone guide me in fixing this issue?
Here is my webpack config
var path = require('path');
var webpack = require('webpack');
var nodeExternals = require('webpack-node-externals');
var browserConfig = {
entry: './src/browser/index.js',
output: {
path: path.resolve(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/',
},
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{
test: /\.css$/,
use: [
'isomorphic-style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
],
},
mode: 'production',
plugins: [
new webpack.DefinePlugin({
__isBrowser__: 'true',
}),
],
};
var serverConfig = {
entry: './src/server/index.js',
target: 'node',
externals: [nodeExternals()],
output: {
path: __dirname,
filename: 'server.js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{
test: /\.css$/,
use: [
'isomorphic-style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
],
},
plugins: [
new webpack.DefinePlugin({
__isBrowser__: 'false',
}),
],
};
module.exports = [browserConfig, serverConfig];
here is my index.js (server)
import express from 'express';
import cors from 'cors';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import serialize from 'serialize-javascript';
import StyleContext from 'isomorphic-style-loader/StyleContext';
import App from '../shared/App';
import routes from '../shared/routes';
const app = express();
app.use(cors());
app.use(express.static('public'));
app.get('*', (req, res, next) => {
const css = new Set(); // CSS for all rendered React components
const insertCss = (...styles) =>
styles.forEach((style) => css.add(style._getCss()));
const activeRoute = routes.find((route) => matchPath(req.url, route)) || {};
const promise = activeRoute.fetchInitialData
? activeRoute.fetchInitialData(req.path)
: Promise.resolve();
promise
.then((data) => {
const context = { data };
const markup = renderToString(
<StyleContext.Provider value={{ insertCss }}>
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
</StyleContext.Provider>
);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>SSR with RR</title>
<script src="/bundle.js" defer></script>
<script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
<style type="text/css">${[...css].join('')}</style>
</head>
<body>
<div id="app">${markup}</div>
</body>
</html>
`);
})
.catch(next);
});
app.listen(3000, () => {
console.log(`Server is listening on port: 3000`);
});
here is my index.js (browser)
import React from 'react';
import { hydrate } from 'react-dom';
import App from '../shared/App';
import { BrowserRouter } from 'react-router-dom';
import StyleContext from 'isomorphic-style-loader/StyleContext';
const insertCss = (...styles) => {
const removeCss = styles.map((style) => style._insertCss());
return () => removeCss.forEach((dispose) => dispose());
};
hydrate(
<StyleContext.Provider value={{ insertCss }}>
<BrowserRouter>
<App />
</BrowserRouter>
</StyleContext.Provider>,
document.getElementById('app')
);
and here is a component inside the App.js which uses the css styling that does not work.
import React from 'react';
import { NavLink } from 'react-router-dom';
import style from './css/style.css';
import withStyles from 'isomorphic-style-loader/withStyles';
function Navbar() {
const languages = [
{
name: 'All',
param: 'all',
},
{
name: 'JavaScript',
param: 'javascript',
},
{
name: 'Ruby',
param: 'ruby',
},
{
name: 'Python',
param: 'python',
},
{
name: 'Java',
param: 'java',
},
];
return (
<ul className='navbar'>
{languages.map(({ name, param }) => (
<li key={param}>
<NavLink
activeStyle={{ fontWeight: 'bold' }}
to={`/popular/${param}`}
>
{name}
</NavLink>
</li>
))}
</ul>
);
}
export default withStyles(style)(Navbar);

I faced the same problem. Problem is related with css-loader. By default, css-loader generates JS modules that use the ES modules syntax. isomorphic-style-loader needs a CommonJS modules syntax.
Try this:
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false,
},
}

Related

ReferenceError: document is not defined when refresh nextjs page

i am trying to create a simple UI library using react for Nextjs 9.4, here what i am doing
// input.js in React UI Lib
import React from "react";
import styled from "./input.module.scss";
const Input = React.forwardRef((props, ref) => (
<>
{props.label && <label className={styled.label}>{props.label}</label>}
<input className={styled.input} {...props} ref={ref} />
</>
));
export default Input;
and made an index to export all modules for simplicity
// app.js the index file for the lib
import PrimaryButton from "./components/button/primaryButton";
import TextInput from "./components/input/input";
import PasswordInput from "./components/passwordInput/password";
import CheckBox from "./components/checkbox/checkbox";
export {
PrimaryButton,
TextInput,
PasswordInput,
CheckBox
};
also here is my webpack config to build for SSR Next
const path = require("path");
const autoprefixer = require("autoprefixer");
const nodeExternals = require("webpack-node-externals");
const CSSLoader = {
loader: "css-loader",
options: {
modules: "global",
importLoaders: 2,
sourceMap: false,
},
};
const CSSModlueLoader = {
loader: "css-loader",
options: {
modules: true,
importLoaders: 2,
sourceMap: false,
},
};
const PostCSSLoader = {
loader: "postcss-loader",
options: {
ident: "postcss",
sourceMap: false,
plugins: () => [autoprefixer()],
},
};
const SassLoader = {
loader: "sass-loader",
options: {
// Prefer `dart-sass`
implementation: require("sass"),
},
};
module.exports = {
target: "node",
entry: "./src/app.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
chunkFilename: "[id].js",
publicPath: "",
library: "",
libraryTarget: "commonjs",
},
externals: [nodeExternals()],
resolve: {
extensions: [".js", ".jsx"],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: /node_modules/,
},
{
test: /\.(sa|sc|c)ss$/i,
exclude: [/node_modules/, /\.module\.(sa|sc|c)ss$/i],
use: ["style-loader", CSSLoader, PostCSSLoader, SassLoader],
},
{
test: /\.module\.(sa|sc|c)ss$/i,
exclude: /node_modules/,
use: ["style-loader", CSSModlueLoader, PostCSSLoader, SassLoader],
},
{
test: /\.(png|jpe?g|gif)$/,
loader: "url-loader?limit=10000&name=img/[name].[ext]",
},
],
},
};
1-i build
2-publush on npm
3-import in Nextjs
then everything works well , but the problem is when i try to refresh (F5) the page during development i get the error
Unhandled Runtime Error
ReferenceError: document is not defined
how can i fix that ?
try to render component only in client side you can do with:
typeof window !== 'undefined' ? <Component /> : null
you are using style-loader in your webpack config, it will inject styles into head using document.createElement that is not availabe in SSR, you can choose other options like mini-css-extract-plugin
const Example = dynamic( () => import('example'), { ssr: false } )
https://github.com/elrumordelaluz/reactour/issues/130
try to check component in client side rendering ex:
const isBrowser = typeof window !== 'undefined';
isBrowser ? <Component/> : null;
another option is try to render using ssr false:
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
Thanks..
You may not always want to include a module on server-side. For example, when the module includes a library that only works in the browser.
Take a look at the following example:
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home

Webpack config for SSR SCSS

I have a React-TypeScript SSR app where I used SCSS files for my styling. I need to write a rule in Webpack to load the SCSS and I haven't been able to do it.
I found various solutions online, all of which are extremely complex and use things like mini-css-extract-plugin. I couldn't get any of them to work.
I currently have two webpack config files, one for the client (web) and one for the server (node), both of which load the SCSS as such:
{
test: /\.scss$/,
use: ["css-loader", "sass-loader"]
}
I also encountered another issue in that I can't use style-loader as it throws an error about the window object. Does anyone have a working example (simple preferably) of loading SCSS in Webpack?
You are on right track with 2 web config file you can use
https://gist.github.com/mburakerman/629783c16acf5e5f03de60528d3139af
But don't set any other config file like babel.rc .yaml etc or other definition in project.json
try this
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader'
]
//..
plugins: [
new MiniCssExtractPlugin({
filename: 'assets/css/bundle-[contenthash].css',
chunkFilename: 'assets/css/bundle-[contenthash].css'
})
],
Look full example https://github.com/dewelloper/pzone/blob/master/webpack.config.store.js
A boilerplate for server-side rendering using react, webpack, Sass
(for both css-modules and pure sass)
webpack.config.js
const path = require('path');
const isDevelopment = true;
module.exports = [
{
name: 'client',
target: 'web',
entry: './client.jsx',
output: {
path: path.join(__dirname, 'static'),
filename: 'client.js',
publicPath: '/static/',
},
resolve: {
extensions: ['.js', '.jsx']
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules\/)/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]",
},
sourceMap: isDevelopment,
}
},
{
loader: 'sass-loader'
}
]
}
],
},
},
{
name: 'server',
target: 'node',
entry: './server.jsx',
output: {
path: path.join(__dirname, 'static'),
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: '/static/',
},
devtool: 'source-map',
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules\/)/,
use: [
{
loader: 'babel-loader',
}
]
},
{
test: /\.scss$/,
use: [
{
loader: 'isomorphic-style-loader',
},
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]",
},
sourceMap: isDevelopment,
}
},
{
loader: 'sass-loader'
}
]
}
],
},
}
];
dev dependencies:
npm i -D #babel/cli #babel/preset-es2015 #babel/core #babel/plugin-proposal-class-properties #babel/preset-env #babel/preset-react babel-core babel-loader babel-plugin-lodash babel-plugin-react-transform babel-preset-env babel-preset-es2015 babel-preset-react babel-preset-stage-0 css-loader express isomorphic-style-loader node-sass sass-loader style-loader webpack webpack-dev-middleware webpack-hot-middleware webpack-hot-server-middleware
and dependencies:
npm i react react-dom react-helmet react-router-dom
sever.jsx:
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import {Helmet} from "react-helmet";
import Template from './template';
import App from './App';
export default function serverRenderer({ clientStats, serverStats }) {
return (req, res, next) => {
const context = {};
const markup = ReactDOMServer.renderToString(
<StaticRouter location={ req.url } context={ context }>
<App />
</StaticRouter>
);
const helmet = Helmet.renderStatic();
res.status(200).send(Template({
markup: markup,
helmet: helmet,
}));
};
}
App.jsx:
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Menu from './Menu'
import Helmet from "react-helmet";
import homepageStyles from './homepage.scss';
class Homepage extends Component {
render() {
return (
<div className={ homepageStyles.component }>
<Helmet
title="Welcome to our Homepage"
/>
<Menu />
<h1>Homepage</h1>
</div>
);
}
}
class About extends Component {
render() {
return (
<div>
<h1>About</h1>
</div>
);
}
}
class Contact extends Component {
render() {
return (
<div>
<h1>Contact</h1>
</div>
);
}
}
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Helmet
htmlAttributes={{lang: "en", amp: undefined}} // amp takes no value
titleTemplate="%s | React App"
titleAttributes={{itemprop: "name", lang: "en"}}
meta={[
{name: "description", content: "Server side rendering example"},
{name: "viewport", content: "width=device-width, initial-scale=1"},
]}
/>
<Switch>
<Route exact path='/' component={ Homepage } />
<Route path="/about" component={ About } />
<Route path="/contact" component={ Contact } />
</Switch>
</div>
);
}
}
template.jsx
export default ({ markup, helmet }) => {
return `<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="root">${markup}</div>
<div>Heeeeeeeeeeeeeeeeeeeelmet</div>
<script src="/static/client.js" async></script>
</body>
</html>`;
};
menu.jsx:
import { Link } from 'react-router-dom';
import React, { Component } from 'react';
import './menu.scss';
class Menu extends Component {
render() {
return (
<div>
<ul>
<li>
<Link to={'/'}>Homepage</Link>
</li>
<li>
<Link to={'/about'}>About</Link>
</li>
<li>
<Link to={'/contact'}>Contact</Link>
</li>
</ul>
</div>
);
}
}
export default Menu;
.babelrc:
{
"presets": [
"#babel/react",
"#babel/preset-env"
],
"plugins": [
"#babel/proposal-class-properties"
]
}
homepage.sccs
.component {
color: blue;
}
menu.scss:
li {
background-color: yellow;
}
I used this article:
https://blog.digitalkwarts.com/server-side-rendering-with-reactjs-react-router-v4-react-helmet-and-css-modules/
Webpack Community now details an approach for SSR sass compiled via webpack in mini-css-extract-plugin/#recommend
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== "production";
module.exports = {
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
],
},
plugins: [].concat(devMode ? [] : [new MiniCssExtractPlugin()]),
};
Note that style-loader should not be used in an SSR app or in a webpack production build because injects CSS into the DOM. MiniCSSExtractPlugin is recommended for SSR production builds and should not be used with style-loader (window will not be defined in webpack's node-based prod build).

Webpack and React BrowserRouter

im working on a project that was made in react with hashrouter, i want to change to browserouter but the project already has a webpack config and im kind of new to it, i know i should make webpack to take all calls to index (since im getting Cannot get on all routes) but i cant find any info on this kind of setup:
This is the current webpack config
const path = require('path');
const webpack = require('webpack');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
devtool: 'cheap-module-source-map',
entry: {
app: [
'webpack-hot-middleware/client',
'react-hot-loader/patch',
'./src/app'
]
},
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.jsx', '.scss'],
alias: {
'react-native': 'react-native-web'
}
},
output: {
path: path.join(__dirname, 'public/assets'),
publicPath: '/assets/',
filename: '[name].bundle.js'
},
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{
loader: 'react-hot-loader/webpack'
}, {
loader: 'babel-loader', options: {cacheDirectory: '.babel-cache'}
}]
}, {
// Most react-native libraries include uncompiled ES6 JS.
test: /\.js$/,
include: [
/node_modules\/react-native-/,
/node_modules\/react-router-native/,
/node_modules\/#indec/
],
loader: 'babel-loader',
query: {
presets: ['react-app'],
cacheDirectory: '.babel-cache'
}
}, {
test: /\.scss$/,
loader: [
'css-hot-loader'
].concat(
ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
)
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, {
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.json$/,
/\.s?css$/,
/\.(jpg|png)/
],
loader: 'url-loader',
options: {name: '[name].[ext]', limit: 10000}
}, {
test: /\.(jpg|png)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
options: {name: '[name].[ext]'}
}]
},
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify(require('./package.json').version),
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
ENDPOINT: JSON.stringify(require('./config.json').endpoint)
}),
new webpack.optimize.CommonsChunkPlugin('vendor'),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new FriendlyErrorsWebpackPlugin(),
new ExtractTextPlugin('[name].bundle.css')
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
app gets initiated on this index.js file
const path = require('path');
const app = require('connect')();
const config = require('./config.json');
const winston = require('winston');
const PORT = process.env.PORT || config.server.port;
process.env.NODE_ENV = config.mode;
app.use(require('morgan')(config.server.morgan));
app.use(require('compression')());
app.use(require('serve-static')(path.join(__dirname, config.server.static)));
if (config.mode === 'development') {
const config = require('./webpack.config');
const compiler = require('webpack')(config);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath,
}));
app.use(require('webpack-hot-middleware')(compiler));
}
require('http').createServer(app).listen(
PORT,
() => winston.info('Server started at port %s', config.server.port)
);
and my app js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Aux from 'react-aux';
import Home from '../Home';
import Admin from '../Admin';
import SignIn from '../SignIn';
import Header from './Header';
import Footer from './Footer';
const App = () => (
<BrowserRouter>
<Aux>
<Header/>
<main>
<Switch>
<Route path="/" component={Home}/>
<Route path="/admin" component={Admin}/>
<Route path="/signIn" component={SignIn}/>
</Switch>
</main>
</Aux>
</BrowserRouter>
);
export default App;
I end up finding a easy solution, just created a express app and handled all 404 to index, didn't find how to do it with connect.
const path = require('path');
const express = require('express');
const app = express();
const config = require('./config.json');
const winston = require('winston');
const PORT = process.env.PORT || config.server.port;
process.env.NODE_ENV = config.mode;
app.use(require('morgan')(config.server.morgan));
app.use(require('compression')());
app.use(require('serve-static')(path.join(__dirname, config.server.static)));
if (config.mode === 'development') {
const config = require('./webpack.config');
const compiler = require('webpack')(config);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath,
}));
app.use(require('webpack-hot-middleware')(compiler));
}
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, './public/index.html'), function(err) {
if (err) {
res.status(500).send(err);
}
});
});
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
require('http').createServer(app).listen(
PORT,
() => winston.info('Server started at port %s', config.server.port)
);
You can use historyApiFallback: true in your config to do this.
Docs on that are located here

React Router can't configure URLs on localhost and remote. Receiving 404 not found error

I've encountered weird situation. I believe that the issue is related to React Router V4 configuration.
I'm using the react-router-modal and React Router v4. With react-router-modal component I render a link to a modal window which have it's own unique URL. Once a link to a modal is clicked - the modal is opened and the URL is added to the address bar. So I could even access the modal window from a new tab with this url:http://localhost:8080/modal_1URL what is very crucial for me.
In dev mode (npm start) everything works fine, also once the invalid URL is entered the page just reloads and invalid URL is remained in address bar. (Not best case)
I thought that everything will work as it is in a production build. But here is a problems. Once the final build is uploaded to the remote server or localhost I get these errors:
1. Once the invalid URL is entered - I receive 404 Not Found
2. Once I try to access a modal with a straight URL (not clicked in loaded page) http://localhost:8080/modal_1 - 404 Not Found
No .htaccess is uploaded
The index.js is very simple and the whole application is onepage with various components:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { ModalContainer } from 'react-router-modal';
import LandingPage from './components/villages/Landing Page.js';
import WOW from 'wowjs';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
class App extends React.Component {
componentDidMount() {
new WOW.WOW().init();
}
render() {
return (
<div>
<LandingPage />
</div>
)
}
}
ReactDOM.render(
<I18nextProvider i18n={i18n}>
<BrowserRouter>
<div>
<App />
<ModalContainer />
</div>
</BrowserRouter>
</I18nextProvider>,
document.getElementById('app')
);
And the component witch renders the modals looks like:
import React from 'react';
import ReactDOM from 'react-dom';
import { ModalRoute, ModalLink } from 'react-router-modal';
import { Link } from 'react-router-dom';
import ImageLoader from 'react-load-image';
function Preloader(props) {
return <img src="img/spinner.gif" className="img-responsive center-block image-loader" />;
}
function ModalOne(props) {
const { t } = props;
return (
<div className='basic__modal-content'>
...
</div>
);
}
const ExtendedModalOne = translate()(ModalOne);
class Items extends React.Component {
render() {
const { t } = this.props;
return (
<div className="container">
<ul>
<div id="works">
<li>
<Link to="/modal_1"> <img src="img/" /> </Link>
<h3>Name</h3>
</li>
</div>
</ul>
<ModalLink component={ExtendedModalOne} path={`/modal_1`} />
<ModalLink component={ExtendedModalTwo} path={`/modal_2`} />
</div>
)
}
}
module.exports = translate()(Items);
and webpack.config:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const StyleExtHtmlWebpackPlugin = require('style-ext-html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const autoprefixer = require('autoprefixer');
const staticSourcePath = path.join(__dirname, 'css');
const sourcePath = path.join(__dirname);
const buildPath = path.join(__dirname, 'dist');
module.exports = {
stats: {
warnings: false
},
devtool: 'cheap-module-source-map',
devServer: {
historyApiFallback: true,
contentBase: './'
},
entry: {
app: path.resolve(sourcePath, 'index.js')
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[chunkhash].js',
publicPath: '/'
},
resolve: {
extensions: ['.webpack-loader.js', '.web-loader.js', '.loader.js', '.js', '.jsx'],
modules: [
sourcePath,
path.resolve(__dirname, 'node_modules')
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.[chunkhash].js',
minChunks (module) {
return module.context && module.context.indexOf('node_modules') >= 0;
}
}),
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer({
browsers: [
'last 3 version',
'ie >= 10'
]
})
],
context: staticSourcePath
}
}),
new webpack.HashedModuleIdsPlugin(),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'index.html'),
path: buildPath,
excludeChunks: ['base'],
filename: 'index.html',
minify: {
collapseWhitespace: true,
collapseInlineTagWhitespace: true,
removeComments: true,
removeRedundantAttributes: true
}
}),
new PreloadWebpackPlugin({
rel: 'preload',
as: 'script',
include: 'all',
fileBlacklist: [/\.(css|map)$/, /base?.+/]
}),
new webpack.NoEmitOnErrorsPlugin(),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
threshold: 10240,
minRatio: 0.8
})
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
},
include: sourcePath
},
{
test: /\.(eot?.+|svg?.+|ttf?.+|otf?.+|woff?.+|woff2?.+)$/,
use: 'file-loader?name=assets/[name]-[hash].[ext]'
},
{
test: /\.(png|gif|jpg|svg)$/,
use: [
'url-loader?limit=20480&name=assets/[name]-[hash].[ext]'
],
include: staticSourcePath
}
]
}
};
Your HTTP server should always send index.html file for any route.
Example NodeJS Express configuration:
// ...
const app = express();
app.get('*', (req, res) => {
res.sendFile('path/to/your/index.html')
})
// ...
As I know, for the Apache server you can use:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
It tells to Apache server to rewrite everything to the index.html page file and let the client handle routing.

How to use CSS Modules with webpack in React isomorphic app?

I am build an isomorphic app using react, react-router, express and webpack. Now I want to use css modules to import css.
I use import './index.css' in index.jsx, it works fine on client, but doesn't work on server rendering. The error is Error: Cannot find module './index.css'.
components/index.jsx
import React, {Component, PropTypes} from 'react';
import style from './index.css';
class App extends Component {
constructor(props, context) {
super(props, context);
}
render() {
return (
<div id="login">
// ...
</div>
);
}
};
export default App;
server/router/index.js
import url from 'url';
import express from 'express';
import swig from 'swig';
import React from 'react';
import {renderToString} from 'react-dom/server';
import {match, RouterContext} from 'react-router';
import routes from '../../client/routes/routes';
import DataWrapper from '../../client/container/DataWrapper';
import data from '../module/data';
const router = express.Router();
router.get('*', async(req, res) => {
match({
routes,
location: req.url
}, async(error, redirectLocation, props) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.status(302).redirect(redirectLocation.pathname + redirectLocation.search);
} else if (props) {
let content = renderToString(
<DataWrapper data={data}><RouterContext {...props}/></DataWrapper>
);
let html = swig.renderFile('views/index.html', {
content,
env: process.env.NODE_ENV
});
res.status(200).send(html);
} else {
res.status(404).send('Not found');
}
});
});
export default router;
webpack.config.dev.js(for webpack-dev-server)
var webpack = require('webpack');
var config = require('./config');
module.exports = {
devtool: 'inline-source-map',
entry: [
'webpack-dev-server/client?http://localhost:' + config.webpackPort,
'webpack/hot/only-dev-server',
'./src/client/entry',
],
output: {
path: __dirname + '/public/js',
filename: 'app.js',
publicPath: 'http://localhost:' + config.webpackPort + '/public/js',
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify('development')
}
})
],
resolve: {
extensions: ['', '.js', '.jsx', '.css']
},
module: {
loaders: [{
test: /\.jsx?$/,
loader: 'react-hot',
exclude: /node_modules/
}, {
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules',
exclude: /node_modules/
}, {
test: /\.(png|woff|woff2|svg|ttf|eot)$/,
loader: 'url-loader',
exclude: /node_modules/
}]
}
}
I'd recommend using webpack to compile UI code for both client and server side in that case. Just set target: "node" in webpack config to produce bundle which can executed in Node environment.
That article might help for compiling your server side code with Webpack: http://jlongster.com/Backend-Apps-with-Webpack--Part-I
Especially on how to exclude node_modules with the externals key.
A very bare config might look like:
'use strict';
const path = require('path');
const fs = require('fs');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const rootDir = path.resolve(__dirname, '..');
const distDir = path.join(rootDir, 'dist');
const srcDir = path.join(rootDir, 'src');
const localStyles = new ExtractTextPlugin('local.css', { allChunks: true });
const nodeModules = fs.readdirSync('node_modules')
.filter(dir => !dir.startsWith('.'))
.reduce((acc, prop) => {
acc[prop] = 'commonjs ' + prop;
return acc;
}, {});
const loaders = [
{
test: /\.(js|jsx)$/,
include: srcDir,
exclude: /node_modules/,
loader: 'babel',
query: {
cacheDirectory: true,
},
},
{
test: /\.css$/,
include: srcDir,
loader: localStyles.extract(
'style',
'css?modules&localIdentName=[name]-[local]_[hash:base64:5]'
),
},
{
test: /\.json$/,
loader: 'json',
},
];
module.exports = {
target: 'node',
entry: {
server: ['server/index'],
},
output: {
path: distDir,
filename: '[name].bundle.js',
},
externals: nodeModules,
module: {
loaders,
},
plugins: [
localStyles,
],
};
Another solution (Webpack free) could be to use babel-plugin-css-modules-transform
.

Resources