t.createWindow error when upgrade to next.js 15.2.5 or 15.3.1
"isomorphic-dompurify": "^2.21.0", "next": "15.3.1", "react": "19.1.0", "react-dom": "19.1.0",
[email protected]: dependencies: dompurify: 3.2.4 jsdom: 26.0.0
Stack Trace
TypeError: s is not a function
./node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/jsdom/browser/Window.js in t.createWindow at line 132:1
Object.defineProperty(window, globalName, propDesc);
}
}
// Create instances of all the web platform interfaces and install them on the window.
installInterfaces(window, ["Window"]);
// Now we have an EventTarget contructor so we can work on the prototype chain.
// eslint-disable-next-line func-name-matching, func-style
const WindowConstructor = function Window() {
./node_modules/.pnpm/[email protected]/node_modules/jsdom/lib/api.js in new w at line 36:1
const mimeType = new MIMEType(options.contentType === undefined ? "text/html" : options.contentType);
const { html, encoding } = normalizeHTML(input, mimeType);
options = transformOptions(options, encoding, mimeType);
this[window] = createWindow(options.windowOptions);
const documentImpl = idlUtils.implForWrapper(this[window]._document);
options.beforeParse(this[window]._globalProxy);
./node_modules/.pnpm/[email protected]/node_modules/isomorphic-dompurify/index.js in global.DOMPurify at line 1:223
{snip} ("dompurify")):function(){const r=e(require("dompurify")),{JSDOM:u}=e(require("jsdom")),{window:o}=new u("<!DOCTYPE html>");return r(o)}());
./node_modules/.pnpm/[email protected]/node_modules/isomorphic-dompurify/index.js in exports.modules at line 1:261
{snip} ("dompurify")):function(){const r=e(require("dompurify")),{JSDOM:u}=e(require("jsdom")),{window:o}=new u("<!DOCTYPE html>");return r(o)}());
Resolving isomorphic-dompurify Errors in Next.js 15.3.x+
Hi @flanker! I noticed you're running into an issue with isomorphic-dompurify in your Next.js project. Let me help you resolve this problem.
The Issue
There's a compatibility issue between newer versions of Next.js (15.3.x), React 19, and isomorphic-dompurify, resulting in the following error:
TypeError: s is not a function
at t.createWindow (.next/server/chunks/3828.js:793:17818)
at new w (.next/server/chunks/3828.js:802:194312)
...
...
...
This appears to be caused by synchronization issues between server and client components when using isomorphic-dompurify for HTML sanitization.
Environment
{
"next": "15.3.2",
"react": "19.1.0",
"react-dom": "19.1.0",
"isomorphic-dompurify": "2.24.0"
}
Dependencies:
- [email protected] depends on:
- dompurify: 3.2.5
- jsdom: 26.1.0
Solution
The best way to resolve this issue is by dynamically importing isomorphic-dompurify inside a React hook that runs only on the client side:
import { useEffect, useState } from 'react';
export const useSanitize = (dirtyHtml?: string) => {
const [sanitizedHtml, setSanitizedHtml] = useState('');
useEffect(() => {
const importDOMPurify = async () => {
const DOMPurify = (await import('isomorphic-dompurify')).default;
setSanitizedHtml(DOMPurify.sanitize(dirtyHtml ?? ''));
};
importDOMPurify();
}, []);
return sanitizedHtml;
};
Usage Example
// In your component
const sanitizedHtml = useSanitize(content);
// In your JSX
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
This approach ensures that isomorphic-dompurify is only loaded and executed in the client environment, avoiding the server/client hydration conflicts that cause the error.
I hope this will help you resolve the issue!
- Edit:
I'm not sure why this only happens in production (after build).
If anyone has insights into this, please share them with us.
This is happening for me in Next.js 15.1.4 now. I think it maybe because of the latest release.
@Ho-s If you want to load a component for client only, you may use modern Next.js approach like "use client" at the top of the component file or the good old lazy-loading with ssr: false.
No need to develop workarounds with useEffect() anymore.
Same problem. This bug leads to memory leak. A large number of TypeError instances are being created, which are not cleared and end up cluttering the memory.
I've been running into this quite a bit recently, so after a lot of digging, I discovered that Next/Webpack may sometimes (not sure based on what heuristic) bundle jsdom and split the modules in such way that there are circular dependencies during initialization. As a result, installInterfaces was somehow being called before the module which exports it finished initializing, causing the TypeError.
I worked around this by updating the Webpack config to explicity specify "jsdom" as an "external" dependency on the server. Ex:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (isServer) {
config.externals.push('jsdom');
}
return config;
},
};
So far, this seems to have fixed the error for me 🤞
I’m using Next.js 15.5.6, and the workaround didn't work for me until I updated my package.json to override the parse5 version:
"pnpm": {
"overrides": {
"parse5": "^7.1.2"
}
}
After that, I ran pnpm install, and in production, I just redeployed , everything worked fine.
@issam-seghir Why not parse5: ^8.0.0? Did you try it?
@issam-seghir Why not parse5: ^8.0.0? Did you try it?
They were switched to ESM-only in the latest version (v8), which is actually the root cause of the issue. So, it's not directly related to jsdom.
Downgrading to the previous version fixed the problem for me because it still uses require() instead of ESM.
Got it @issam-seghir. Thanks. Makes sense