A modern, lightweight, TypeScript virtual DOM for Node.js, browser, and static content generation.
- β‘οΈ Fast: Efficient HTML parsing and serialization
- π§© JSX Compatible: Works seamlessly with JSX/TSX
- π CSS Selectors: Query with a subset of CSS selectors
- π Easy Manipulation: Chainable API,
.handle()helper, and more - π HTML, XML, Markdown, Plaintext: Serialize to multiple formats
- π§Ή Pretty Print: Tidy up HTML with
tidyDOM - π¦Ύ TypeScript: Full typings, modern codebase
- π Safe HTML: Output sanitized HTML for user content
Note: This project does not aim for full browser DOM completeness, but covers most practical use cases for static content, SSR, and offline DOM manipulation.
npm i zeed-domUsed by TipTap in its html-package.
- Virtual DOM tree with
VNode,VElement,VDocument, etc. - HTML parsing and serialization
- XML output support
- CSS selector engine (subset)
- JSX/TSX support (see below)
- Safe HTML serialization (
serializeSafeHTML) - Markdown and plaintext serialization
- Manipulation helpers:
.handle(),.replaceWith(),.remove(), etc. - Works in Node.js, browser, and serverless
- Pretty print HTML (
tidyDOM) - TypeScript-first API
Drop in HTML, query, and change it. Returns HTML again. Great for post-processing:
import { handleHTML } from 'zeed-dom'
const newHTML = handleHTML(html, (document) => {
const img = document.querySelector('.img-wrapper img')
if (img)
img.setAttribute('title', img.getAttribute('src'))
})Take any HTML node or document and serialize it to another format:
serializePlaintext(node): Readable and searchable plain textserializeMarkdown(node): Simple MarkdownserializeSafeHTML(node)orsafeHTML(htmlString): Allow only basic tags and attributes
import { h, xml } from 'zeed-dom'
const dom = h(
'ol',
{ class: 'projects' },
[
h('li', null, 'zeed ', h('img', { src: 'logo.png' })),
h('li', null, 'zeed-dom'),
]
)
console.log(dom.render())
// <ol class="projects"><li>zeed <img src="logo.png"></li><li>zeed-dom</li></ol>
console.log(dom.render(xml))
// <ol class="projects"><li>zeed <img src="logo.png" /></li><li>zeed-dom</li></ol>import { h } from 'zeed-dom'
let dom = (
<ol className="projects">
<li>zeed</li>
<li>zeed-dom</li>
</ol>
)
dom.handle('li', (e) => {
if (!e.textContent.endsWith('-dom')) {
e.remove()
} else {
e.innerHTML = '<b>zeed-dom</b> - great DOM helper for static content'
}
})
console.log(dom.render())
// <ol class="projects"><li><b>zeed-dom</b> - great DOM helper for static content</li></ol>import { tidyDOM, vdom } from 'zeed-dom'
const dom = vdom('<div>Hello World</div>')
tidyDOM(dom)
console.log(dom.render())
// Output is pretty printed like:
// <div>
// Hello World
// </div>JSX is supported out of the box. For TypeScript, add to your tsconfig.json:
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h"
}
}Add this to your shims.d.ts:
// https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements
declare namespace JSX {
interface IntrinsicElements {
[elemName: string]: any
}
}For ESBuild:
{
jsxFactory: 'h'
}Or as a CLI option: --jsx-factory=h
For browser DOM:
const { hFactory } = require('zeed-dom')
export const h = hFactory({ document })vdom(htmlString): Parse HTML to virtual DOMtidyDOM(node): Pretty print/format DOMserializeSafeHTML(node): Output safe HTMLserializeMarkdown(node): Output MarkdownserializePlaintext(node): Output plain texthandleHTML(html, fn): Manipulate HTML with a callbackVElement,VNode,VDocument, etc.: Core classes.handle(selector, fn): Manipulate elements by selector.querySelector,.querySelectorAll: CSS selector queries.replaceWith(),.remove(),.setAttribute(), etc.: DOM-like methods
The parser is fast, as shown in htmlparser-benchmark
- Use double underscore in JSX for namespaces:
<xhtml__link />β<xhtml:link /> - Use
CDATAhelper for raw data:<div>{CDATA(yourRawData)}</div> styleattributes can be objects:<span style={{backgroundColor: 'red'}} />β<span style="background-color: red" />- Works in Node.js, browser, and serverless
- TypeScript-first, but works with plain JS too