Skip to content

Server‐Side Rendering

David Ortner edited this page Jan 20, 2025 · 23 revisions

Simple Example

import { Window } from "happy-dom";

const window = new Window({
	innerWidth: 1024,
	innerHeight: 768,
	url: "http://localhost:8080",
});
const document = window.document;

document.write(`
    <html>
        <head>
             <title>Test page</title>
        </head>
        <body>
            <div class="root"></div>
            <script src="app.js"></script>
        </body>
    </html>
`);

// Waits for all async operations to complete
// Note that this may get stuck when using intervals or a timer in a loop
await window.happyDOM.waitUntilComplete();

// Outputs the rendered result
console.log(window.document.documentElement.outerHTML);

// Cancels all ongoing operations and destroys the Window instance
await window.happyDOM.close();

Web Compontents

Happy DOM supports rendering custom elements (web components) server-side using a new web feature called Declarative Shadow DOM.

import { Window } from "happy-dom";

const window = new Window({
	innerWidth: 1024,
	innerHeight: 768,
	url: "http://localhost:8080",
});
const document = window.document;

document.write(`
<html>
   <head>
      <title>Test page</title>
   </head>

   <body>
      <div>
         <my-custom-element>
            <span>Slotted content</span>
         </my-custom-element>
      </div>
      <script>
         class MyCustomElement extends HTMLElement {
            constructor() {
               super();
               this.attachShadow({ mode: "open", serializable: true });
            }

            connectedCallback() {
               this.shadowRoot.innerHTML = \`
                  <style>
                     :host {
                        display: inline-block;
                        background: red;
                     }
                  </style>
                  <div><slot></slot></div>
               \`;
            }
         } 

         customElements.define("my-custom-element", MyCustomElement);
      </script>
   </body>
</html>
`);

// Waits for all async operations to complete
// Note that this may get stuck when using intervals or a timer in a loop
await window.happyDOM.waitUntilComplete();

/*
Will output:
<my-custom-element>
    <span>Slotted content</span>
    <template shadowroot="open" shadowrootserializable="">
        <style>
            :host {
                display: inline-block;
                background: red;
            }
        </style>
        <div><slot></slot></div>
    </template>
</my-custom-element>
*/
console.log(
	document.body
		.querySelector("div")
		.getHTML({ serializableShadowRoots: true })
);

// Cancels all ongoing operations and destroys the Window instance
await window.happyDOM.close();

Virtual Server

A virtual server makes it possible to simulate an HTTP server that serves files from the local file system.

By using a virtual server, we can for example server side render the build result of bundlers such as Vite and Webpack.

Read more about virtual server

File structure

  • {root}
    • build
      • index.html
      • style.css
      • script.js
import { Window, IVirtualServer } from "happy-dom";

const browser = new Browser({
   settings: {
      fetch: : {
         virtualServers: [
         {
            url: /https:\/\/localhost:8080\/[a-z]{2}\/[a-z]{2}\//,
            directory: "./build"
         }
       ]
      }
   }
});

const page = browser.newPage();

await page.goto("https://localhost:8080/gb/en/");

await page.waitUntilComplete();

// Outputs the rendered result
console.log(
   document.documentElement.getHTML({ serializableShadowRoots: true })
);

await browser.close()
Clone this wiki locally