mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmSecurityupdated 22 Sept 2026

isomorphic-dompurify review

isomorphic-dompurify 3.23.0 gives browser and Node code one DOMPurify import. A browser build uses the real window; the Node path creates a jsdom window because DOMPurify sanitizes a DOM tree rather than parsing strings alone. The wrapper exposes sanitize(), hooks, configuration, DOMPurify types, an explicit-window factory, and clearWindow() for long-running servers. Version 3.23.0 updates DOMPurify and development dependencies without changing that surface. In our measured 3.22.0 install, both CommonJS require and ESM import worked, and the browser bundle was 11 KB gzipped.

Verdict

isomorphic-dompurify 3.22.0 occupied 29 MB across 40 packages in our sandbox, while its browser path measured 11 KB gzipped and had 0 audit findings. Install the current 3.23.0 wrapper when one DOMPurify policy must span SSR and browser code; browser-only projects should use DOMPurify directly.

We installed it

Lab card: what happened when we installed isomorphic-dompurifyScreenshot of isomorphic-dompurify documentation
Install✓ · 3.1s40 packages on disk · 29 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser11 KBgzipped (28.6 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does isomorphic-dompurify install cleanly?

Yes. In a fresh container with an empty cache, npm install isomorphic-dompurify finished in 3 seconds, leaving 40 packages and 29 MB on disk. npm audit reported no known vulnerabilities.

How much does isomorphic-dompurify add to a browser bundle?

11 KB gzipped (28.6 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does isomorphic-dompurify work with both ESM and CommonJS?

Yes. Both import 'isomorphic-dompurify' and require('isomorphic-dompurify') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does isomorphic-dompurify include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

isomorphic-dompurify or dompurify: which should you use?

dompurify: Use it for browser-only sanitization or when you prefer to create and own the Node DOM instance. isomorphic-dompurify 3.22.0 occupied 29 MB across 40 packages in our sandbox, while its browser path measured 11 KB gzipped and had 0 audit findings.

When should you not use isomorphic-dompurify?

Sanitization happens only in a browser; direct DOMPurify avoids installing jsdom for a server path you will never use

API stability3/5Version 3.23.0 preserves the wrapper's sanitize, hook, configuration, factory, type, and clearWindow interfaces. The project explicitly says DOMPurify does not follow semantic versioning, so every upstream change is released here as a minor because a patch may contain more than a behavior-preserving fix. Pinning only the major therefore accepts policy or output changes that an authentication or content pipeline may notice.
Docs4/5The README lists the exact Node engine matrix, explains why jsdom exists, shows default and named imports, documents the explicit-window factory, re-exported hook types, server cleanup, worker limits, and a current CommonJS failure mode. Sanitizer options and security caveats live in the upstream DOMPurify documentation, so a reader must use two READMEs and test which conditional export a framework actually selects.
Maintenance5/5GitHub shows a push on 2026-08-25, 594 stars, and 2 open issues and pull requests in an unarchived repository. Release 3.23.0 shipped the same day with a DOMPurify update from 3.4.13 to 3.4.14 plus toolchain dependency bumps. Rapid sanitizer updates are useful when upstream fixes land, though this project's minor-version policy makes regression testing part of routine maintenance.
Ecosystem5/5The npm endpoint counted 5,562,675 downloads during the latest completed week. Conditional exports cover Node and browser targets, both require() and ESM import passed in our 3.22.0 check, and TypeScript declarations are bundled. Framework playgrounds cover Astro, Next.js, Nuxt, React, and Svelte. The cost of that cross-runtime reach is jsdom, which made our server-side install 29 MB.

Use it if

  • The same HTML-sanitizing module must run during Node server rendering and in a browser
  • DOMPurify hooks, profiles, Trusted Types output, and TypeScript types are needed on both sides of an application
  • A server process can carry jsdom and periodically reset the wrapper's internal window
  • The deployment already runs a Node release accepted by the package's narrow engine range
Skip it if

Setup reality

We installed isomorphic-dompurify 3.22.0 in a fresh unprivileged Node 22 Bookworm container with 3 CPUs and 8 GB of RAM. npm completed in 3.1 seconds and left 40 packages using 29 MB on disk. The wrapper itself was 56 KB unpacked with 2 direct dependencies and no peers. npm audit reported 0 known vulnerabilities. The package requires Node ^22.22.2, ^24.15.0, or >=26.0.0.

Version 3.22.0 is CommonJS with an exports map that selects Node or browser entry files. require() and ESM import worked in our sandbox, and TypeScript declarations are included. A full browser import measured 28.6 KB minified and 11 KB gzipped. The browser branch avoids jsdom at runtime, while the Node installation still contains the server DOM dependency.

Node sanitization reuses an internal jsdom window. The project documents progressive slowdown and memory growth as DOM state accumulates in a long-running process. clearWindow() closes that window and creates a fresh one. It also discards hooks and persistent setConfig() state, so reapply policy after cleanup. Decide whether cleanup happens after each request or after a bounded batch based on measured traffic and allocation cost.

The current 3.23.0 release updates DOMPurify from 3.4.13 to 3.4.14 according to its release notes. This wrapper publishes upstream DOMPurify changes as minor versions because it cannot assume upstream patches preserve behavior. Pin the minor, keep malicious HTML fixtures for the exact allowlist, and review upgrades. The README also records a CommonJS ERR_REQUIRE_ESM case with a jsdom override; reproduce the production module mode before relying on that workaround.

Patterns

Clean untrusted markup sanitize-html

import DOMPurify from 'isomorphic-dompurify';

const clean = DOMPurify.sanitize(userHtml);
renderTrustedMarkup(clean);

sanitize() returns markup under DOMPurify's default policy. Perform this at the final HTML sink and avoid adding attributes or nodes afterward.

Limit output to HTML html-profile

import { sanitize } from 'isomorphic-dompurify';

const clean = sanitize(input, {
  USE_PROFILES: { html: true },
});

The HTML profile excludes SVG and MathML. USE_PROFILES takes precedence over ALLOWED_TAGS rather than merging with it.

Permit a small formatting set allow-formatting

const clean = sanitize(input, {
  ALLOWED_TAGS: ['p', 'strong', 'em', 'a'],
  ALLOWED_ATTR: ['href'],
});

A short allowlist narrows accepted markup. Test every URL scheme and output context that the application permits.

Remove style markup forbid-inline-style

const clean = sanitize(input, {
  FORBID_TAGS: ['style'],
  FORBID_ATTR: ['style'],
});

FORBID_TAGS and FORBID_ATTR add restrictions to the selected policy. Later code can still introduce unsafe style content.

Remove link targets with a hook add-hook

import { addHook, removeHook, sanitize, type NodeHook } from 'isomorphic-dompurify';

const stripTarget: NodeHook = node => {
  if ('removeAttribute' in node) node.removeAttribute('target');
};
addHook('afterSanitizeAttributes', stripTarget);
try {
  sanitize(input);
} finally {
  removeHook('afterSanitizeAttributes', stripTarget);
}

Hooks modify the shared sanitizer instance. Remove a request-scoped hook in finally, and reapply persistent hooks after clearWindow().

Log removed elements during debugging inspect-removals

DOMPurify.sanitize(input);
for (const removal of DOMPurify.removed) {
  console.debug(removal);
}

DOMPurify documents removed as diagnostic data from the latest sanitization call. Do not treat that list as proof that output is safe.

Apply a temporary shared policy set-config

import { clearConfig, sanitize, setConfig } from 'isomorphic-dompurify';

setConfig({ USE_PROFILES: { html: true } });
try {
  sanitize(input);
} finally {
  clearConfig();
}

setConfig() creates shared mutable state and causes per-call configuration to be ignored. Avoid changing it concurrently across requests.

Reset the server DOM after a batch clear-window

import { clearWindow, sanitize } from 'isomorphic-dompurify';

try {
  for (const row of rows) row.safeHtml = sanitize(row.html);
} finally {
  clearWindow();
}

clearWindow() closes and replaces the Node jsdom window. It is a browser no-op and removes hooks and persistent config on the server.

Bind a sanitizer to an explicit window isolated-instance

import DOMPurify from 'isomorphic-dompurify';
import { JSDOM } from 'jsdom';

const dom = new JSDOM('');
try {
  const purify = DOMPurify(dom.window);
  purify.sanitize(input);
} finally {
  dom.window.close();
}

The factory creates an instance for the supplied window. When you construct jsdom yourself, closing that window is your responsibility.

Return a document fragment dom-fragment

const fragment = sanitize(input, {
  RETURN_DOM_FRAGMENT: true,
});
inspect(fragment.childNodes);

Returned nodes belong to the active browser or jsdom window. Do not keep server nodes after clearWindow() closes their owner.

Request TrustedHTML output trusted-types

const trusted = sanitize(input, {
  RETURN_TRUSTED_TYPE: true,
});
element.innerHTML = trusted;

TrustedHTML is available only where the Trusted Types API exists. The page's CSP and policy setup still control whether the sink accepts it.

Lock sanitizer behavior with a fixture regression-test

import { expect, test } from 'vitest';
import { sanitize } from 'isomorphic-dompurify';

test('drops click handlers', () => {
  const clean = sanitize('<button onclick="steal()">Pay</button>');
  expect(clean).not.toContain('onclick');
});

Wrapper minor releases can carry DOMPurify behavior changes. Keep malicious fixtures for the exact configuration and markup sinks used in production.

Alternatives

PackageRegistryPick it when
dompurifynpmUse it for browser-only sanitization or when you prefer to create and own the Node DOM instance
sanitize-htmlnpmUse it for a Node-centered sanitizer with explicit tags, attributes, iframe hosts, and URL-scheme policy
xssnpmUse it when a smaller whitelist sanitizer matches the application's HTML and parser requirements

More security guides

cryptography · pyjwt · jose · requests-oauthlib · oauthlib · dompurify · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.