Server-Side DOM Shim
server-dom-shim is a utility package designed to provide a minimal shim for standard DOM APIs within server-side rendering (SSR) environments. Its primary purpose is to prevent common errors like `HTMLElement is not defined` when rendering web components or other DOM-dependent JavaScript on the server, without incurring the overhead of a full browser emulation library like JSDOM. The package is currently at version 1.1.0 and has seen a consistent, albeit not rapid, release cadence with updates in late 2025 and mid-2024. A key differentiator is its use of Node.js conditional exports, which intelligently exports `@lit-labs/ssr-dom-shim` in Node.js environments while deferring to native DOM APIs in browser contexts. This approach allows developers to write universal codebases that seamlessly adapt to different execution environments, making it suitable for modern SSR frameworks and libraries that leverage web standards. It focuses on providing necessary global definitions rather than full interactive DOM capabilities.
Common errors
-
ReferenceError: HTMLElement is not defined
cause Attempting to instantiate or reference `HTMLElement` (or similar DOM globals) in a Node.js environment without a DOM shim.fixInstall `server-dom-shim` and import the necessary DOM APIs, ensuring your SSR entry point runs after the shim has been loaded. -
TypeError: (0, import_server_dom_shim.HTMLElement) is not a constructor (or similar `require` errors in ESM projects)
cause Attempting to import named exports from `server-dom-shim` using CommonJS `require()` syntax in an ESM project, or vice versa.fixEnsure you are using `import { HTMLElement } from 'server-dom-shim';` for ESM projects. `server-dom-shim` is designed for ESM usage. -
Cannot find module 'server-dom-shim' (during build with Vite, Rollup, etc.)
cause Build tools sometimes have issues resolving conditional exports or specific module resolutions, especially in earlier versions of bundlers or specific configurations. Version 1.0.2 explicitly mentions a 'vite build issue workaround'.fixEnsure your build tool (e.g., Vite) is updated to a recent version. If the problem persists, check the `server-dom-shim` GitHub issues or documentation for specific build tool configurations, or consider adjusting your bundler's module resolution settings.
Warnings
- gotcha This package provides a *shim* for DOM APIs, not a full browser environment. It does not support rendering, layout, or interactive browser-specific functionalities (e.g., `document.body.appendChild()` leading to visual updates). It primarily provides constructors and prototypes to prevent `ReferenceError` during SSR.
- gotcha The package uses Node.js conditional exports, which means the actual shim (`@lit-labs/ssr-dom-shim`) is only active in Node.js environments. In browser environments, it exports native DOM APIs. This can lead to subtle behavioral differences between environments if not understood.
- breaking While v1.1.0 added comprehensive exports for 'all DOM and CSS APIs', older versions might have had a more limited set of shims. If upgrading from pre-1.1.0 versions, ensure that any previously missing APIs are now correctly shims rather than relying on custom workarounds.
Install
-
npm install server-dom-shim -
yarn add server-dom-shim -
pnpm add server-dom-shim
Imports
- HTMLElement
const { HTMLElement } = require('server-dom-shim')import { HTMLElement } from 'server-dom-shim' - customElements
const { customElements } = require('server-dom-shim')import { customElements } from 'server-dom-shim' - Event
import Event from 'server-dom-shim'
import { Event, CustomEvent } from 'server-dom-shim'
Quickstart
import { HTMLElement, customElements, Event, CustomEvent, Element } from 'server-dom-shim';
// Verify that HTMLElement is defined in the server environment
console.log('HTMLElement is defined:', typeof HTMLElement !== 'undefined');
// Example: Define a simple custom element (though it won't render visually in Node.js)
class MyCustomElement extends HTMLElement {
constructor() {
super();
// In a real SSR scenario, this would be part of a component's lifecycle
// and prevent errors when a framework tries to instantiate it.
console.log('MyCustomElement instance created (server-side shim)');
}
connectedCallback() {
console.log('MyCustomElement connectedCallback (server-side shim)');
}
}
// Define the custom element if not already defined (important for re-runs in dev)
if (!customElements.get('my-custom-element')) {
customElements.define('my-custom-element', MyCustomElement);
console.log('Custom element "my-custom-element" defined (server-side shim)');
}
// Example: Create an event instance
const myEvent = new CustomEvent('my-custom-event', { detail: { data: 'hello' } });
console.log(`Created event: ${myEvent.type} with detail:`, myEvent.detail);
// This shim allows libraries that expect a DOM environment to run without errors during SSR.