-
-
Notifications
You must be signed in to change notification settings - Fork 274
Server Renderer
@happy-dom/server-renderer is a tool for using Happy DOM for server-side rendering (SSR) or static site generation (SSG).
The benefit of using this tool compared to other SSR and SSG solutions, is that it is not tied to a specific framework. For example, you can use React, Vue, Angular and Lit on the same page. The page is rendered as a whole and outputted as HTML.
This tool uses a worker pool by default to render multiple pages in parallel. Each worker can also render multiple pages in parallel that shares resources.
npm install @happy-dom/server-renderer --save-devSee all available command line arguments in the CLI Documentation.
This example will output the result to the file ./happy-dom/render/gb/en/index.html.
npx @happy-dom/server-renderer "https://example.com/gb/en/"or if you have it installed
happy-dom-sr "https://example.com/gb/en/"Show more command line examples
This will output the result to the file ./happy-dom/render/gb/en/index.html.
Read more about the configuration file in the CLI Documentation.
happy-dom-sr --config=<path-to-config-file> "https://example.com/gb/en/"This will setup a virtual server that serves the content from the directory "./build" for requests towards https://example.com/{cc}/{lc}/.
The result will be saved to the file "./happy-dom/render/gb/en/index.html".
happy-dom-sr --browser.fetch.virtualServer="https:\/\/example\.com\/[a-z]{2}\/[a-z]{2}\/"|"./build" "https://example.com/gb/en/"Read more about virtual servers in the API Documentation for Happy DOM.
This will start a proxy server that proxies requests from https://localhost:3000 to https://example.com.
happy-dom-sr --server --server.targetOrigin="https://example.com"Some pages may have never ending timer loops causing the rendering to never complete or there may be other issues preventing the page from rendering correctly. You can enable debugging by adding the flag "--debug".
Setting the log level to 4 (debug) will provide with even more information (default is 3).
Note that debug information is collected after the page rendering has timed out. The default rendering timeout is 30 seconds and can be changed using the flag "--render.timeout".
happy-dom-sr --debug --logLevel=4 "https://example.com/gb/en/"See all available configuration options in the API Documentation.
import { ServerRenderer } from "@happy-dom/server-renderer";
const renderer = new ServerRenderer({
// Your configuration options
});
const results = await renderer.render(["https://example.com/gb/en/"]);
// The rendered HTML
console.log(results[0].url);
console.log(results[0].content);Show more Javascript examples
This will setup a virtual server that serves the content from the directory "./build" for requests towards https://example.com/{cc}/{lc}/.
The result will be saved to the files "./index_gb.html" and "./index_us.html".
import { ServerRenderer } from "@happy-dom/server-renderer";
const renderer = new ServerRenderer({
browser: {
fetch: {
virtualServers: [
{
url: /https:\/\/example\.com\/[a-z]{2}\/[a-z]{2}\//,
directory: "./build",
},
],
},
},
});
await renderer.render([
{
url: "https://example.com/gb/en/",
outputFile: "./index_gb.html",
headers: { "X-Test": "true" },
},
{ url: "https://example.com/us/en/", outputFile: "./index_us.html" },
]);Read more about virtual servers in the API Documentation for Happy DOM.
Some pages may have never ending timer loops causing the rendering to never complete or there may be other issues preventing the page from rendering correctly. You can enable debugging by setting the configuration debug to "true".
Setting the log level to 4 (debug) will provide with even more information (default is 3).
Note that debug information is collected after the page rendering has timed out. The default rendering timeout is 30 seconds and can be changed using the flag "--render.timeout".
import {
ServerRenderer,
ServerRendererLogLevelEnum,
} from "@happy-dom/server-renderer";
const renderer = new ServerRenderer({
debug: true,
logLevel: ServerRendererLogLevelEnum.debug,
render: {
timeout: 10000, // 10 seconds
},
});
const results = await renderer.render([["https://example.com/gb/en/"]]);When used on a server, you may want to keep the workers alive between renders to avoid the overhead of starting new workers.
import { ServerRenderer } from "@happy-dom/server-renderer";
const results = await renderer.render(
["https://example.com/gb/en/", "https://example.com/us/en/"],
{ keepAlive: true }
);
for (const result of results) {
console.log(result.url);
console.log(result.content);
}
// When you are done with rendering, close the workers
await renderer.close();Start a proxy server that proxies requests from https://localhost:3000 to https://example.com.
import { ServerRendererServer } from "@happy-dom/server-renderer";
const server = new ServerRendererServer({
server: {
serverURL: "https://localhost:3000/gb/en/",
targetOrigin: "https://example.com",
},
});
await server.start();Detecting Server Environment
You may want to detect if the code is running in a server environment or in a browser. You can do this by checking navigator.userAgent.
function isServerRendering() {
return navigator.userAgent.includes("HappyDOM");
}Static Site Generation (SSG) with Vite or Webpack
This example shows how to statically render (SSG) your application after it has been built using Vite or Webpack.
The easiest way to simulate a server environment is to use the browser setting fetch.virtualServers to serve the built files using a virtual server. Read more about virtual servers in the API Documentation for Happy DOM.
We will use Vite in this example, but the same principles apply to Webpack.
The Vite config file looks like this:
import { defineConfig } from "vite";
export default defineConfig({
build: {
rollupOptions: {
input: {
home: "./src/index.html",
about: "./src/about/index.html",
},
},
},
});We will build the application using the command:
npx vite buildThe built files are outputted to the directory ./dist.
We want to deploy the rendered files to a server where the home page is available at https://example.com/{cc}/{lc}/ and the about page at https://example.com/{cc}/{lc}/about/. We want the application to be able to support translations by path, so country and language code has also been added to the URL ({cc} and {lc}).
Lets create a config file for the server-renderer called server-renderer.config.ts:
import type { IOptionalServerRendererConfiguration } from "@happy-dom/server-renderer";
export default <IOptionalServerRendererConfiguration>{
browser: {
fetch: {
virtualServers: [
{
url: /https:\/\/example\.com\/[a-z]{2}\/[a-z]{2}\//,
directory: "./dist",
},
],
},
},
render: {
// Serialize shadow DOMs with the "serializable" option set to true
// This is only needed if you want to render Web Components with shadow DOMs
serializableShadowRoots: true,
},
urls: [
// Home page for US and SE
"https://example.com/us/en/",
"https://example.com/se/sv/",
// About page for US and SE
"https://example.com/us/en/about/",
"https://example.com/se/sv/about/",
],
};Now we can render the pages using the following CLI command:
npx @happy-dom/server-renderer --config=./server-renderer.config.tsor if you have it installed
happy-dom-sr --config=./server-renderer.config.tsNext and final step is to upload the rendered files from the directory ./happy-dom/render/ to your server, together with the static assets from the directory ./dist/static/.
Add meta tags for SEO
When using server rendering, you may want to add meta tags for SEO purposes. You can do this by adding the meta tags to the head of your HTML document.
The meta tags will be available in the rendered HTML if they are applied by a script in the application being rendered.
const meta = document.createElement("meta");
meta.name = "description";
meta.content = "Your description here";
document.head.appendChild(meta);Performance Tips
Adding server-side rendering to your application can reduce the time to first meaningful paint, but will by default increase the overall load for the client. Here are some tips to improve the performance of your server-side rendering:
- Use CSS files: Using CSS files instead of inline styles will reduce the size of the HTML document, however, it may cause layout shifts if the CSS file is large and takes time to load. You can mitigate this by inlining critical CSS and loading the rest of the CSS asynchronously.
-
Only render what is needed: Avoid rendering parts of the page that are not needed. You can detect if it is a server environment by checking
navigator.userAgentand conditionally render parts of the page. - Minimize JavaScript: Minimize the amount of JavaScript that is executed on the client on first load. This will reduce the time to interactive and improve the overall performance of the page. You can use code splitting and lazy loading to achieve this.
-
Micro Frontends using Web Components: One way to structure your application is to use micro frontends using Web Components. The configuration
render.serializableShadowRootstells the server render to serialize shadow DOMs with the serializable option set to true. -
Cache rendered pages: You can use a CDN or a caching layer to achieve this. Use cache headers like
Cache-Controlto control how long the rendered HTML should be cached.
Help Packages