*How SPA Works
*Example of SPA
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render( // π
<App />,
document.getElementById('root')
);PROs
CONs
What is the server side rendering (SSR)?
Server side rendering is a technique to pre-render full
HTML page on the server before sending it to the client.
*Using SPA approach
*Using Server side rendering approach
*Example of SSR. Server code.
import express from "express";
import ReactDOMServer from "react-dom/server";
import App from "../src/App";
const app = express();
const router = express.Router();
router.use("^/$", (req, res, next) => { // π routing rules
fs.readFile(path.resolve("./build/index.html"), "utf8", (err, data) => {
if (err) {
return res.status(500).send("An error occurred");
}
return res.send(
data.replace(
'<div id="root"></div>',
`<script type="application/javascript">
window.__initialProps = ${JSON.stringify(initialProps)}
</script>
<div id="root">${ReactDOMServer.renderToString(<App {...initialProps} />)}</div>` // π
)
);
});
});
app.use(router);
// ...
app.listen(8080, () => console.log(`SSR running on port 8080`));*Need to run a NodeJS server (Express, Koa)
*Example of SSR. Client code
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const initialProps = window.__initialProps; // get copy of props
ReactDOM.hydrate( // π
<App {...initialProps} />,
document.getElementById("root")
);*SSR & Hydration (Send pre-rendered HTML + copy of props)
Why we need React hydrate at all? π€
ReactDOM.hydrate() is same as ReactDOM.render() , but it is used to hydrate (attach event listeners) a container whose HTML contents were rendered by ReactDOMServer.
React will attempt to attach event listeners to the existing markup.
Make components rendered on the server
alive in browser.
*SSR & Hydration (Closer look)
βΉοΈ
*Method of passing copy of props depends on the implementation, examples:
PROs
CONs
Usually publicly accessible indexed pages (SEO) are mostly static.
*Example of page/components structure
*Using SSR + FULL hydration
*Using SSR + PARTIAL hydration
*Example of partial hydration. Server code
import express from "express";
import ReactDOMServer from "react-dom/server";
import App from "../src/App";
const app = express();
const router = express.Router();
router.use("^/$", (req, res, next) => { // π routing rules
fs.readFile(path.resolve("./build/index.html"), "utf8", (err, data) => {
if (err) {
return res.status(500).send("An error occurred");
}
return res.send(
data.replace(
'<div id="root"></div>',
// don't need window.__initialProps
`<div id="root">${ReactDOMServer.renderToString(<App {...initialProps} />)}</div>` // π
)
);
});
});
app.use(router);
// ...
app.listen(8080, () => console.log(`SSR running on port 8080`));*Need to run a NodeJS server (Express, Koa)
*Example of partial hydration. Client code
import React from "react";
import { Layout, Section, Navbar, Footer } from "./components"
import { withHydration } from "./hydration"; // π
import "./App.css";
const HydratedNavbar = withHydration(Navbar); // π
const HydratedSection = withHydration(Section); // π
export default function App() {
return (
<Layout className="App">
<HydratedNavbar {...navbarProps} /> // π
<HydratedSection title="Dynamic Section" /> // π
<Section title="Static Section"/>
<Footer {...footerProps} />
</Layout>
);
}*Partial hydration implementation. Simplified
let uniqueId = 0
const withHydration = Component => {
const hydrationId = uniqueId++
if(window === "undefined") {
// executed on the server
return props => (
<>
<script
type="text/json"
data-hydration-id={hydrationId}
dangerouslySetInnerHTML={{ __html: JSON.stringify(props) }}
/>
<div>
<Component {...props} />
</div>
</>
)
}
// executed on the client
const script = document.querySelector(`script[data-hydration-id="${hydrationId}"]`)
const props = JSON.parse(script.innerHTML)
ReactDOM.hydrate(<Component {...props} />, script.nextElementSibling)
}*Edge case. Multiple instances
import React from "react";
import { Layout, Section, Navbar, Footer } from "./components"
import { hydrated } from "./hydration";
import "./App.css";
const HydratedNavbar = withHydration(Navbar);
const HydratedSection = withHydration(Section);
export default function App() {
return (
<Layout className="App">
<Hydrated.Navbar {...navbarProps}/>
<Hydrated.Section title="Dynamic Section" />
<Hydrated.Section title="Dynamic Section 2" /> // π
<Section title="Static Section" />
<Footer {...footerProps} />
</Layout>
);
}*Edge case. Multiple instances. Solution
let uniqueId = 0
const withHydration = Component => {
const hydrationId = uniqueId++
if(window === "undefined") {
// executed on the server
return props => (
<>
<script
type="text/json"
data-hydration-id={hydrationId}
dangerouslySetInnerHTML={{ __html: JSON.stringify(props) }}
/>
<div>
<Component {...props} />
</div>
</>
)
}
// executed on the client. UPDATED
document.querySelectorAll(`script[data-hydration-id="${hydrationId}"]`) // π
.forEach((element) => { // π
const props = JSON.parse(element.innerHTML);
ReactDOM.hydrate(<Component {...props} />, element.nextElementSibling);
});
}*Partial hydration. "hydrated" helper function
import React from "react";
import { Layout, Section, Navbar, Footer } from "./components"
import { hydrated } from "./hydration"; // π helper function
import "./App.css";
const Hydrated = hydrated({ Navbar, Section }); // π
export default function App() {
return (
<Layout className="App">
<Hydrated.Navbar {...navbarProps} />
<Hydrated.Section title="Dynamic Section" />
<Section title="Static Section"/>
<Footer {...footerProps} />
</Layout>
);
}*Partial hydration. "hydrated" helper function. Implementation
export const hydrated = (components) => {
const Hydrated = {};
Object.entries(components).forEach(([name, Component]) => {
Hydrated[name] = withHydration(Component);
});
return Hydrated;
};
*Checkout example in GH
Demo example: www.toptal.com
PROs
CONs
When to use partial hydration
Erzhan Torokulov, Dmitry Kudelko, Pawel Knapik
![]()
Want to be part of the greatest talent network company in the world?
React, DevOps, Automated QA, and many more...