Better handling for window and document objects in SSR environment
npx @tessl/cli install tessl/npm-ssr-window@5.0.0SSR Window provides safe handling for window and document objects in server-side rendering (SSR) environments. Unlike full DOM implementations like JSDOM, it provides minimal patches to prevent SSR applications from throwing errors when accessing browser-specific APIs. This lightweight approach ensures compatibility without the overhead of complete DOM simulation.
npm install ssr-windowimport { getWindow, getDocument, ssrWindow, ssrDocument, extend } from "ssr-window";For CommonJS:
const { getWindow, getDocument, ssrWindow, ssrDocument, extend } = require("ssr-window");import { getWindow, getDocument } from "ssr-window";
// Get safe window and document objects
const window = getWindow();
const document = getDocument();
// Use them safely in SSR environment
window.addEventListener('resize', () => {
console.log('Resize event (safe in SSR)');
});
const div = document.createElement('div');
div.setAttribute('class', 'container');
// Query DOM safely (returns null/empty arrays in SSR)
const elements = document.querySelectorAll('.item');SSR Window is built around two core concepts:
ssrWindow and ssrDocument objects with safe default implementationsextend utility for adding application-specific propertiesSafe window object handling that works in both browser and SSR environments.
/**
* Returns a patched Window object that works in both browser and SSR environments
* @returns Window object extended with SSR-safe properties
*/
function getWindow(): Window;
/**
* Pre-configured window-like object with SSR-safe properties and methods
*/
declare const ssrWindow: {
document: typeof ssrDocument;
navigator: {
userAgent: string;
};
location: {
hash: string;
host: string;
hostname: string;
href: string;
origin: string;
pathname: string;
protocol: string;
search: string;
};
history: {
replaceState(): void;
pushState(): void;
go(): void;
back(): void;
};
CustomEvent: () => any;
addEventListener(): void;
removeEventListener(): void;
getComputedStyle(): {
getPropertyValue(): string;
};
Image(): void;
Date(): void;
screen: {};
setTimeout(): void;
clearTimeout(): void;
matchMedia(): {};
requestAnimationFrame(callback: any): NodeJS.Timeout | null;
cancelAnimationFrame(id: any): void;
};Usage Examples:
import { getWindow, ssrWindow } from "ssr-window";
// Get patched window object (browser window + SSR fallbacks)
const window = getWindow();
window.requestAnimationFrame(() => {
// Safe in both browser and SSR
});
// Access pre-configured SSR window directly
console.log(ssrWindow.navigator.userAgent); // Returns empty string in SSRSafe document object handling for DOM operations in SSR environments.
/**
* Returns a patched Document object that works in both browser and SSR environments
* @returns Document object extended with SSR-safe properties
*/
function getDocument(): Document;
/**
* Pre-configured document-like object with SSR-safe properties and methods
*/
declare const ssrDocument: {
body: {};
addEventListener(): void;
removeEventListener(): void;
activeElement: {
blur(): void;
nodeName: string;
};
querySelector(): null;
querySelectorAll(): any[];
getElementById(): null;
createEvent(): {
initEvent(): void;
};
createElement(): {
children: any[];
childNodes: any[];
style: {};
setAttribute(): void;
getElementsByTagName(): any[];
};
createElementNS(): {};
importNode(): null;
location: {
hash: string;
host: string;
hostname: string;
href: string;
origin: string;
pathname: string;
protocol: string;
search: string;
};
};Usage Examples:
import { getDocument, ssrDocument } from "ssr-window";
// Get patched document object (browser document + SSR fallbacks)
const document = getDocument();
const element = document.createElement('div'); // Safe in SSR
element.setAttribute('class', 'container');
// Access pre-configured SSR document directly
const results = ssrDocument.querySelectorAll('.items'); // Returns [] in SSRUtility for safely extending objects with additional properties, used internally and available for custom extensions.
/**
* Safely extends target object with source object properties
* Recursively merges objects while avoiding prototype pollution
* @param target - Target object to extend (defaults to empty object)
* @param src - Source object to copy properties from (defaults to empty object)
*/
function extend(target?: any, src?: any): void;Usage Examples:
import { ssrWindow, ssrDocument, extend } from "ssr-window";
// Extend ssrWindow with custom properties
extend(ssrWindow, {
navigator: {
language: 'en-US',
platform: 'linux',
},
customProperty: 'custom value',
});
// Extend ssrDocument with custom properties
extend(ssrDocument, {
body: {
classList: {
add: () => {},
remove: () => {},
},
},
title: 'SSR Page Title',
});
// Use extended objects
console.log(ssrWindow.navigator.language); // 'en-US'
console.log(ssrDocument.title); // 'SSR Page Title'SSR Window is designed to prevent errors rather than throw them:
null or empty arrays instead of throwingsetTimeout/clearTimeoutAll methods are designed to fail silently in SSR environments while working normally in browser environments.