An easy-to-use Virtual DOM built for the web!

Core API

This documentation covers the core public API. All methods and internals work in the browser and directly in Node.js with or without jsdom .


  • VTree: The internal VDOM structure. They are JavaScript objects that represent a DOM node. They store information such as the tagName, what the childNodes are, and more. A reference to a VTree can get you access to the DOM node it represents. They are used throughout the codebase to simplify and abstract the internals away from the DOM, in favor of one that is virtual, hence the V.

    An added bonus to this is that diffHTML can work seamlessly in Node.js without a DOM abstraction such as jsdom.

  • Transaction: An object that represents a render. One is produced every time you call innerHTML, outerHTML, or toString. You don't need to worry about it unless you're trying to deeply understand the library.

innerHTML (mount, input, options)

Compares and updates the contents of mount with the input. Creates a Transaction, invokes middleware , compares old and new markup, and patches the DOM.


Name Description
mount DOM Node or VTree to sync or patch the childNodes of.
input New markup to replace into mount.
options Config options , inner is always true


import { innerHTML } from 'diffhtml';

innerHTML(document.body, `
  <h1>Hello world!</h1>

outerHTML (mount, input, options)

Same as innerHTML except compares the input directly to the mount, instead of the childNodes.

Replaces the contents of a DOM node with the passed in markup, only updates what has changed. Additionally updates the attributes of the parent. If the element you're modifying has a parent, you may also change the element type, but this can sometimes result in unexpected behavior.


Name Description
mount DOM Node or VTree to sync or patch.
input New markup to replace into mount.
options Config options , inner is always false


import { outerHTML } from 'diffhtml';

outerHTML(document.body, `
  <body>Hello world</body>

toString (input, options)

Takes any valid input and returns a serialized string of XML/HTML markup. This helps you create static markup from complex renders to a simple VTree.

All middleware run during this, so features like components and logging work.


Name Description
input New markup to replace into mount.
options Config options , inner and executeScripts have no effect


import { toString } from 'diffhtml';

toString('<body>Hello world</body>');
// <body>Hello world</body>

html (markup)

A tagged template function that parses string markup and creates VTree under-the-hood. Can also be used like a normal function, where you pass markup as the first argument, and values as param2, param3, .... You are able to "interpolate" or mix dynamic values with the HTML string content. This is useful when passing event handlers, objects to DOM elements, passing properties to components, and more.

When you pass a single element and provide newlines and whitespace before and after it, like the examples below, they will be automatically trimmed out of the final tree. If you provide multiple elements, the whitespace will become part of the tree.


The two required inputs are a reference element and new element to compare. Although "element" is used, the actual input can be of various input types all representing an element (or many elements).


A simple example of its usage along with interpolation.

const vTree = html`
    <center style=${{ fontSize: '11px' }}>Hello world</center>

Will basically convert to:

createTree('body', null, [
  createTree('center', { style: { fontSize: '11px' } }, ['Hello world']),

To see how to run this example yourself see the Examples section below.

use (middlewareFunction or middlewareObject)

This function is used to hook plugins into your render pipeline. These plugins are referred to as middleware. They are meant for advanced use cases such as observing the render flow, modifying attributes or elements, and more.

Middleware can be enabled and disabled via code or the browser DevTools.

Refer to the Middleware documentation for documentation on writing your own and find existing plugins.


Name Description
middlewareFunction Use this when you want total control over the task flow. Return inner functions to delve deeper into the flow. Any of the middleware object properties may be attached the function and used together.
middlewareObject Use this when you don't care about the transaction start/stop, and want a cleaner way to monitor the VTree lifecycle.
  • displayName
  • createNodeHook
  • createTreeHook
  • syncTreeHook
  • releaseHook
  • subscribe
  • unsubscribe


Logging the start of a render transaction

function someTask(transaction) {
  console.log('Start of render transaction:', transaction);


Logging the end of a render transaction

function someTask(transaction) {
  console.log('Start of render transaction:', transaction);

  return () => {
    console.log('End of render transaction scheduled');

    // Will fire once the transaction has fully ended.
    transaction.onceEnded(() => {
      console.log('End of render transaction completed');


createTree (nodeName, attributes, ...childNodes)

Creates a Virtual Tree (VTree) which can be interpolated and rendered. This has a similar purpose to hyperscript's h() and React's createElement.


Name Description
nodeName A string for HTML, or couuld be a component or other types like functions when using middleware
attributes An object of DOM attributes or properties key and value or null
...childNodes An array of child nodes, or a single element merged with any additional arguments


const attributes = {
  id: 'test',
  style: 'color: red',

const childNodes = [
  createTree('#text', null, 'Hello world'),

// Create a div Virtual Tree.
const div = createTree('div', attributes, childNodes);

release (mount)

Use this method if you need to clean up memory allocations and anything else internal to diffHTML associated with your element. This is very useful for unit testing and general cleanup when you're done with an element. Applications will probably not use this if the app lives as long as the tab.


Name Description
mount Release memory and all internal references to the DOM node.


These internals are deemed public API and may be relied upon once the library has reached a stable version. The intention is to allow developers to tweak the library, observe and influence internal memory, and build tooling around the runtime.


A JavaScript Set object that contains registered middleware functions that trigger whenever transaction state changes. Use this to help enable/disable middleware functions from running during renders.


A JavaScript Set object that contains functions that trigger whenever a DOM Node is created during the patching process.


A JavaScript Set object that contains functions that trigger whenever a Virtual Tree is created through html, createTree, or during the parsing process.


A JavaScript Set object that contains functions that trigger whenever a Virtual Tree is cleaned up by diffHTML's internal garbage collection. You can do additional cleanup here.


A JavaScript Set object that contains functions that trigger whenever Virtual Trees are compared. You can influence how the Virtual DOM synchronizes changes, by changing attributes JIT, telling diffHTML to ignore certain nodes, or tell diffHTML to not apply any changes to a given node.


A Map that can be used to get access to the DOM node associated to a VTree. This is comparable to findDOMNode in React. Basically if you encounter an object that the documentation says is a VTree and you want to convert to a DOM Node, you could write something like:

import { Internals } from 'diffhtml';

function findDOMNode(vTree) {
  return Internals.NodeCache.get(vTree);


If it comes back as null or undefined then you should take that to mean that the VTree is no longer bound to an element in the DOM. You may also find that diffHTML has re-used the VTree you have saved, so calling NodeCache.get will yield an unexpected result. Therefore its recommended to call this method immediately if you need the DOM node and not save a VTree in between re-renders.


A render transaction is scheduled whenever innerHTML or outerHTML are called. It is an instance of a Transaction class that has a few methods: abort, end, onceEnded, and start. This instance is mutable and the properties will change by the internals. You should not modify the transaction directly unless you know what you're doing. Reading any property is considered the live source-of-truth and a safe operation.

There are a series of tasks that run when a transaction is created. Depending on the build flavor, full or runtime, you will get a different set of tasks. By default transactions run synchronously and you can observe a result immediately after running innerHTML or outerHTML.

> schedule        # If another transaction is running, this will run after
> shouldUpdate    # If nothing has changed, abort early
> parseNewTree    # Full build only, will parse a passed in string of HTML
> reconcileTrees  # Align trees before diffing
> syncTrees       # Loop through and compare trees as efficiently as possible
> patchNode       # Apply chnages to the DOM
> endAsPromise    # If a transaction is delayed, resolve once complete

Transactions have a number of properties available to access:

  • aborted - The transaction was abandoned
  • completed - The transaction successfully completed
  • domNode - The container element being rendered into
  • endedCallbacks - The set of callbacks that will be called once completed
  • input - The raw input to render
  • newTree - The reconciled tree to use for new source-of-truth
  • oldTree - The old tree which may already be updated with newTree
  • options - Options used when updating markup
  • patches - What has been updated in the DOM
  • state - Internal state object for the transaction
  • tasks - Array of functions that were executed when rendering




Property which indicates the current running version of diffHTML.




Allows configuring runtime rendering behavior. These options are accessible via transaction.config and can be set via query string, environment variables, or passing a config object to innerHTML, outerHTML, and toString.

In the case of query string and environment variables, uppercase the variables and prefix with DIFF_. So inner becomes DIFF_INNER. For parser use a JSON string: JSON.stringify({ parser: { rawElements: ['div'] } }).

inner Boolean

Determines if the Transaction should update the DOM Node or just its children. Setting this to true will emulate the behavior of innerHTML and setting it to false emulates outerHTML. You cannot set this using innerHTML or outerHTML, and it has no effect with toString so it is only useful if you manually create Transactions which is an advanced use case.

tasks Function[]

Manipulate the tasks which run. This can allow you to do interesting things with the core API. You can do API changes like providing a stream or generator API for the return signature, you can remove syncing and provide your own object for patching, etc. This feature is used by the project to create the toString method, which changes the return value to a string.

Caution: Only modify this in a closed environment and do not ship components or shared utils which attempt to modify the host tasks.


Change the return value of innerHTML to be a callback.

import { innerHTML, Internals } from 'diffhtml';

// Start with the default tasks.
const newTasks = new Set(Internals.defaultTasks);


// Update the transaction end by returning a callback instead of using a
// Promise based API.
newTasks.add(transaction => {
  const { promises } = transaction;

  // Change the final return value to a callback.
  return callback => {
    if (promises && promises.length) {
      return transaction.promise = Promise.all(promises).then(() => {

    transaction.promise = Promise.resolve(transaction.end());

// You can supress this behavior by setting executeScripts to false
innerHTML(document.body, `<h1>Hello world</h1>`, {
  tasks: [...newTasks],
})(transaction => {
  console.log('Render has completed with transaction', transaction);

executeScripts Boolean

Control whether or not newly appended scripts are executed or not. When enabled, tricks the browser by setting the type property to no-execute when a script is added. This prevents the browser from executing the script.


import { innerHTML } from 'diffhtml';

// By default scripts will execute
innerHTML(document.body, `<script>window.alert('here')</script>`);

// You can supress this behavior by setting executeScripts to false
innerHTML(document.body, `<script>window.alert('here')</script>`, {
  executeScripts: false,

disableMutationObserver Boolean

Allows disabling the MutationObserver feature if the mount is known to never become dirty.


import { innerHTML } from 'diffhtml';

// No MutationObserver will observe if document.body changes outside of diffHTML.
innerHTML(document.body, `Some value`, {
  disableMutationObserver: true,

parser Object

These options modify the parser by changing which elements are treated as block or self closing.

Learn more about these options


This example will throw an error since the parser encountered invalid markup.

import { innerHTML } from 'diffhtml';

innerHTML(document.body, `
  <h1>Hello world</h2>
`, { parser: { rawElements: ['div'] } });

Edit on GitHub