Skip to main content

Getting started

1. Install

npm install --save-dev react-ssr-webpack-plugin

2. Configure ReactSSRWebpackPlugin

In order to output SSR chunks together with CSR chunks, you need to use ReactSSRWebpackPlugin.

webpack.config.js
const {ReactSSRWebpackPlugin, PLUGIN_NAME} = require("react-ssr-webpack-plugin");

module.exports = {
...
"module": {
"rules": [
...
{
"test":/\.css$/,
"use": (info) => {
// You will need this config when you use css modules
const isNode = info.compiler === PLUGIN_NAME;
return (isNode ? [] : [{"loader": MiniCssExtractPlugin.loader}])
.concat([
{
"loader":"css-loader",
"ident": "css-loader",
"options": {
"modules": {
"exportOnlyLocals": isNode, // See https://webpack.js.org/loaders/css-loader/#exportonlylocals for the details
},
},
},
]);
},
},
...
]
},
"plugins": [
...
new ReactSSRWebpackPlugin({
"entry": {
"a.node": path.resolve(__dirname, "a.node")
},
}),
...
]
...
}

3a. Prepare node entry

Here is an example of a entry for SSR. You can use JSX for <head> tag.

a.node.js
import {renderToStaticMarkup, renderToString} from "react-dom/server";
import {App} from "./App";

export default async (props = {}) => { // this needs to be a default and async export
const root = renderToString(<App {...props} />)
const body = "<!DOCTYPE html>" + renderToStaticMarkup(<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII="></link>
<script dangerouslySetInnerHTML={{"__html": `globalThis.props = ${JSON.stringify(props).replace(/</g, "\\u003c")}`}} />
<script dangerouslySetInnerHTML={{"__html": __SOURCES__["a.web.js"]}} />
</head>
<body>
<div id="root" dangerouslySetInnerHTML={{"__html": root}} />
<script integrity={__DIGESTS__["vendors.js"]} src={`${__webpack_public_path__}/${__FILES__["vendors.js"]}`} crossOrigin="anonymous" />
</body>
</html>);

return {
body,
"statusCode": 200,
};
}
note
  • __DIGESTS__, __FILES__ and __SOURCES__ are react-ssr-webpack-plugin specific tokens and they will get replaced at build time.
    • __DIGESTS__ is for subresource integrity purpose.
    • __FILES__ is for getting the hashed filename.
    • __SOURCES__ is for inlining the chunk to the page.

3b. Prepare web entry

a.web.js
import {App} from "./App";
import ReactDOM from "react-dom";

ReactDOM.hydrateRoot(
document.getElementById("root"),
<App {...globalThis.props} />
);

4. Configure ReactSSRMiddleware

In order to simulate SSR, you need to use ReactSSRMiddleware with webpack-dev-server. With webpack-dev-server you can enjoy the live reload when not only the client code but also the server code is change.

webpack.config.js
{
"devServer": {
"onAfterSetupMiddleware": (devServer) => {
devServer.app.get("*", ReactSSRMiddleware(
devServer.compiler,
{
"reqToProps": (req) => ({"url": url.parse(req.originalUrl, true)})
}
));
},
},
}

5. Check the output



<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII="/>
<style>
/*!******************************************************************************************!*\
!*** css ./node_modules/css-loader/dist/cjs.js??css-loader!./examples/app/1.0/a/App.css ***!
\******************************************************************************************/
* {
margin: 0;
}

html,
body,
body > div {
height: 100%;
}
</style>
<script>
globalThis.props = {
"url": {
"protocol": null,
"slashes": null,
"auth": null,
"host": null,
"port": null,
"hostname": null,
"hash": null,
"search": null,
"query": {},
"pathname": "/a.node",
"path": "/a.node",
"href": "/a.node"
},
"__VERSION__": "2.0.development"
}
</script>
<script>
/******/
(()=>{
// webpackBootstrap ...
)();
</script>
</head>
<body>
<div id="root">
<div class="common__DivWrapper-sc-1o9hep8-0 App__DivWrapperA-sc-lq4rwr-0 jbWOMA izvJxW">
...
</div>
</div>
<script integrity="sha256-Yx4g0nYq75xTOA78jk1GNpDSxEXPGApmhIhok8luDPM=" src="vendors.26771103e691bfcf006c.js" crossorigin="anonymous"></script>
</body>
</html>