Server-side Rendering

The most common use case for server-side rendering is to handle the initial render when a user (or search engine crawler) first requests your app.

When the server receives the request, it renders the required component(s) into an HTML string, and then sends it as a response to the client. From that point on, the client takes over rendering duties.

We are using ReactJSS (css-in-js styling solution) to styling our components. And since Material-UI v4 was using the same styling solution with such a User-friendly API that led to great DX (Developer Experience), we have decided to use an API which is similar to Material-UI's.

Sonnat-UI on the Server

It's important to provide the page with the required CSS, otherwise the page will render with just the HTML then wait for the CSS to be injected by the client, causing it to flicker (FOUC). To inject the style down to the client, we need to:

  1. Create a fresh, new ServerStyleSheets instance on every request.
  2. Render the React tree with the server-side collector.
  3. Pull the CSS out.
  4. Pass the CSS along to the client.

On the client side, the CSS will be injected a second time before removing the server-side injected CSS.

Setting Up

Theme Object

Create a theme object that will be shared between the client and the server. If you don't provide a theme object the theming will be defaults to Default Theme.
(For more detailed information about theming, take a look at this doc.)

// file: theme.js
import createTheme from "@sonnat/ui/styles/createTheme";
// Create a theme instance.
const theme = createTheme({
// Your theme options
});
export default theme;

Server-side

The first thing that we need to do on every request is create a new ServerStyleSheets. When rendering, we will wrap App, the root component, inside a <SonnatInitializer> to make the style configuration and the theme available to all components in the component tree.

The key step in server-side rendering is to render the initial HTML of the component before we send it to the client side. To do this, we use ReactDOMServer.renderToString(). We then get the CSS from the sheets using sheets.toString().
The final step on the server-side is to inject the initial component HTML and CSS into a template to be rendered on the client side.

// file: server.js
import express from "express";
import React from "react";
import ReactDOMServer from "react-dom/server";
import ServerStyleSheets from "@sonnat/ui/styles/ServerStyleSheets";
import SonnatInitializer from "@sonnat/ui/styles/SonnatInitializer";
import CssBaseline from "@sonnat/ui/CssBaseline";
import App from "./App";
import theme from "./theme";
function renderFullPage(html, css) {
return `
<!DOCTYPE html>
<html>
<head>
<title>My page</title>
<style id="sonnat-jss-ssr">${css}</style>
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`;
}
function handleRender(req, res) {
const sheets = new ServerStyleSheets();
// Render the component to a string.
const html = ReactDOMServer.renderToString(
sheets.collect(
<SonnatInitializer theme={theme}>
<CssBaseline />
<App />
</SonnatInitializer>
)
);
// Grab the CSS from the sheets.
const css = sheets.toString();
// Send the rendered page back to the client.
res.send(renderFullPage(html, css));
}
const app = express();
app.use("/build", express.static("build"));
// This is fired every time the server-side receives a request.
app.use(handleRender);
const port = 3000;
app.listen(port);
Check out the documentations of <SonnatInitializer> and <CssBaseline>, to learn more about them.

Client-side

The client side is straightforward. All we need to do is remove the server-side generated CSS.

// file: client.js
import React from "react";
import ReactDOM from "react-dom";
import SonnatInitializer from "@sonnat/ui/styles/SonnatInitializer";
import CssBaseline from "@sonnat/ui/CssBaseline";
import App from "./App";
import theme from "./theme";
function Main() {
React.useEffect(() => {
const jssStyles = document.querySelector("#sonnat-jss-ssr");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<SonnatInitializer theme={theme}>
<CssBaseline />
<App />
</SonnatInitializer>
);
}
ReactDOM.hydrate(<Main />, document.querySelector("#root"));

Next.js

We host nextjs implementation which you can find in the GitHub repository under the /examples folder.