
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@naverpay/safe-html-react-parser
Advanced tools
A secure wrapper for html-react-parser with isomorphic-dompurify that automatically sanitizes HTML before parsing.
A secure wrapper for html-react-parser with DOMPurify that automatically sanitizes HTML before parsing.
<custom> safelynpm install @naverpay/safe-html-react-parser
For server-side rendering, you need to install one of the following DOM implementations:
# Option 1: jsdom (most complete, heavier)
npm install jsdom
# Option 2: happy-dom (faster, lighter, recommended)
npm install happy-dom
# Option 3: linkedom (fastest, lightest)
npm install linkedom
The library will automatically detect and use the first available implementation in this order: jsdom → happy-dom → linkedom.
import { safeParse } from '@naverpay/safe-html-react-parser'
// Basic usage - automatically sanitizes dangerous HTML
const Component = () => {
const maliciousHtml = '<p>Hello <script>alert("XSS")</script>World</p>'
return <div>{safeParse(maliciousHtml)}</div>
}
// Result: <div><p>Hello World</p></div>
safeParse(htmlString, options?)Parses HTML string into React elements with automatic XSS protection.
htmlString (string): The HTML string to parseoptions (SafeParseOptions, optional): Configuration optionsinterface SafeParseOptions extends HTMLReactParserOptions {
// DOMPurify configuration
sanitizeConfig?: DOMPurify.Config
// Custom tags to preserve during sanitization
preserveCustomTags?: string[]
}
React elements or array of React elements
import { safeParse } from '@naverpay/safe-html-react-parser'
const html = '<div class="content"><style>body{color:red}</style><p>Text</p></div>'
const result = safeParse(html, {
sanitizeConfig: {
ALLOWED_TAGS: ['div', 'p', 'style'], // Allow style tags
ALLOWED_ATTR: ['class'],
ALLOW_ARIA_ATTR: true
}
})
Use preserveCustomTags to preserve project-specific tags that would otherwise be removed:
import { safeParse } from '@naverpay/safe-html-react-parser'
// Preserve custom tags like <g>, <path>, etc.
const svgContent = '<g><path d="M10,10 L20,20"/></g>'
const result = safeParse(svgContent, {
preserveCustomTags: ['g', 'path'],
replace: (domNode) => {
if (domNode.name === 'g') {
return <g {...domNode.attribs}>{/* custom rendering */}</g>
}
if (domNode.name === 'path') {
return <path {...domNode.attribs} />
}
}
})
All html-react-parser options are supported:
import { safeParse } from '@naverpay/safe-html-react-parser'
const html = '<div id="content"><p>Hello</p><img src="image.jpg" alt="test"/></div>'
const result = safeParse(html, {
replace: (domNode) => {
if (domNode.name === 'img') {
return <img {...domNode.attribs} loading="lazy" />
}
},
trim: true
})
You have two ways to configure the DOM implementation:
Pass domPurifyOptions directly to safeParse():
import { safeParse } from '@naverpay/safe-html-react-parser'
import { Window } from 'happy-dom'
const result = safeParse(htmlString, {
domPurifyOptions: {
domWindowFactory: () => new Window(),
enableCache: true,
maxCacheSize: 100
}
})
Configure once at app initialization:
import { configureDOMPurify } from '@naverpay/safe-html-react-parser'
// Using jsdom
import { JSDOM } from 'jsdom'
configureDOMPurify({
domWindowFactory: () => new JSDOM('<!DOCTYPE html>'),
enableCache: true,
maxCacheSize: 100,
recreateInterval: 1000 // Recreate DOM instance every 1000 sanitizations
})
// Using happy-dom (recommended for better performance)
import { Window } from 'happy-dom'
configureDOMPurify({
domWindowFactory: () => new Window(),
enableCache: true,
recreateInterval: 500
})
// Using linkedom (fastest, minimal footprint)
import { parseHTML } from 'linkedom'
configureDOMPurify({
domWindowFactory: () => parseHTML('<!DOCTYPE html>'),
enableCache: true
})
[!NOTE]
If you don't configure anything, the library will automatically try jsdom → happy-dom → linkedom in that order.
By default, the following HTML tags are allowed:
ALLOWED_TAGS: [
'p', 'br', 'strong', 'em', 'b', 'i', 'u', 'span', 'div',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h',
'ul', 'ol', 'li', 'dl', 'dt', 'dd',
'a', 'img'
]
By default, caching is enabled to improve performance:
configureDOMPurify({
enableCache: true, // Default: true
maxCacheSize: 100, // Default: 100
})
The DOM instance is automatically recreated periodically to prevent memory leaks:
configureDOMPurify({
recreateInterval: 1000 // Default: 1000 sanitization calls
})
| Implementation | Speed | Memory | Completeness | Recommended For |
|---|---|---|---|---|
| jsdom | Slower | Higher | Most complete | Maximum compatibility |
| happy-dom | Fast | Medium | Good | Balanced (Recommended) |
| linkedom | Fastest | Lowest | Basic | Performance-critical apps |
<script>, <iframe>, <object> are automatically removedonclick, onload are stripped outMIT
FAQs
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.