# Server-Side Rendering
Superglue's generators does not include Server Side Rendering, but we
can add support using [Humid](https://github.com/thoughtbot/humid), a SSR library
built for Superglue.
Follow the [instructions](https://github.com/thoughtbot/humid#installation).
Then, if you're using esbuild, create a `app/javascript/server_rendering.js`:
```js
import React from 'react';
import { Application } from '@thoughtbot/superglue';
import { buildVisitAndRemote } from './application_visit';
import { pageIdentifierToPageComponent } from './page_to_page_mapping';
import { store } from './store'
import { renderToString } from 'react-dom/server';
require("source-map-support").install({
retrieveSourceMap: filename => {
return {
url: filename,
map: readSourceMap(filename)
};
}
});
setHumidRenderer((json, baseUrl, path) => {
const initialState = JSON.parse(json)
return renderToString(
,
{
concurrentFeatures: false,
}
)
})
```
Next
```terminal
yarn add esbuild-plugin-polyfill-node text-encoding whatwg-url
```
and add a esbuild build file.
```js
import * as esbuild from 'esbuild'
import { polyfillNode } from "esbuild-plugin-polyfill-node";
await esbuild.build({
entryPoints: ['app/javascript/server_rendering.js'],
bundle: true,
platform: "browser",
define: {
"process.env.NODE_ENV": '"production"'
},
sourcemap: true,
outfile: 'app/assets/builds/server_rendering.js',
logLevel: "info",
loader: {
".js": "jsx",
".svg": "dataurl"
},
inject: ["./shim.js"],
plugins: [
polyfillNode({
globals: false
}),
]
})
```
Add a `shim.js` for the above. We'll need this for the v8 environment that mini-racer runs on.
```javascript
export {TextEncoder, TextDecoder} from 'text-encoding'
export { URL, URLSearchParams } from 'whatwg-url'
export function MessageChannel() {
this.port1 = {
postMessage: function (message) {
console.log('Message sent from port1:', message);
},
};
this.port2 = {
addEventListener: function (event, handler) {
console.log(`Event listener added for ${event} on port2`);
this._eventHandler = handler;
},
removeEventListener: function (event) {
console.log(`Event listener removed for ${event} on port2`);
this._eventHandler = null;
},
simulateMessage: function (data) {
if (this._eventHandler) {
this._eventHandler({ data });
}
},
};
}
export const navigator = {language: "en-us"}
```
Add a line to your `package.json` like so:
```diff
"scripts": {
+ "build:ssr": "node ./build-ssr.mjs"
```
Use `Humid.render` in all your `html` templates, e.g., `index.html.erb` or `superglue.html.erb`:
```diff
-
+ <%= Humid.render(initial_state, request.scheme + '://' + request.host_with_port, request.fullpath).html_safe %>
```
!> Do not render spacing inside of ``. If you do, React will not hydrate properly and
warn `Hydration failed because the initial UI does not match what was rendered on the server`
Change your `application.js` to use `hydrateRoot`:
```diff
- import { createRoot } from 'react-dom/client';
+ import { hydrateRoot } from 'react-dom/client';
```
and change the rest of `application.js` accordingly. For example:
```js
import React from 'react';
import { Application, VisitResponse } from '@thoughtbot/superglue';
import { hydrateRoot } from 'react-dom/client';
import { buildVisitAndRemote } from './application_visit';
import { pageIdentifierToPageComponent } from './page_to_page_mapping';
import { store } from './store'
if (typeof window !== "undefined") {
document.addEventListener("DOMContentLoaded", function () {
const appEl = document.getElementById("app");
const location = window.location;
if (appEl) {
hydrateRoot(appEl,
);
}
});
}
```
and add build script your `package.json` to build both the client and server js bundles. For example:
```
"build": "yarn run build:web && yarn run build:ssr",
"build:web": "esbuild app/javascript/application.js --bundle --sourcemap --outdir=app/assets/builds --loader:.js=jsx --loader:.svg=dataurl --public-path=/assets",
"build:ssr": "node ./build-ssr.mjs",
```