Babel Plugin Jsx Dom Expressions Versions Save

A JSX to DOM plugin that wraps expressions for fine grained change detection


3 years ago

Lots of fixes and updates thanks to all the contributions. Major updates since the last minor version:

  • Drastic updates to TypeScript types thanks to @rbiggs, @Gustorn, @r0skar
  • Improved detection of dynamic expressions by using AST grammar
  • Ternary & Boolean detection for more efficient expression evaluation
  • Improved handling of static expressions (strings, number, template literals)
  • Skip event handlers during SSR
  • Allow arbitrary deep namespaces on Component tag, thanks to @mduclehcm
  • Properly handle children on intrinsic elements, thanks to @mduclehcm
  • Add hydration event execution into compiled output (addresses uncanny valley during hydration) with @mduclehcm


3 years ago
  • Change template cloning to support rehydration of SVG partials
  • Change compiler to batch wrap non-insert template updates
  • Optimize textContent binding


3 years ago

Breaking Change - Removal of Explicit Binding "Parens" Syntax

The new heuristic for how bindings will be reactive will work as follows:

There are certain types of syntax that will never be reactive like function declarations, literal values.

// these bindings never need to be wrapped
<div onClick={() => setState({clicked: true})} name={"mydiv"} />

Similarly since reactivity requires access only function calls or property accesses can be reactive. So simple variables will never need to be wrapped

// also never needs to be wrapped
<div name={myName} maxlength={count * 2} />

So we can limit wrapping to only expressions that could be reactive like:

// wrap because they could be reactive
<div name={} title={getTitle()} />

Also for Component props that take Components like fallback or children we will wrap any expression with a tag to allow deferred evaluation.

// these will be wrapped
<Show when={state.accepted} fallback={<Denied />}>
  <Accepted />

What if you wish for these things to be static? Hoist the value access. Since normal variables aren't reactive just assign it to a variable. This is JSX so we have all the power of JavaScript at our side and do not need a special syntax.

const name =;
// not wrapped as simple variable
<div name={name} />

As a result of this change alwaysWrap option has been removed.


3 years ago

SSR (Experimental)

This release introduces 2 additional compiler modes for SSR support. You enable them by the generate option. One is for server-side rendering (ssr) and the other does hydration on the client(hydrate).

Always Wrap

Sometimes for certain libraries the {( )} syntax is more of a detractor than the large performance gains it brings. With always wrap you do not need use that syntax for the expression to be wrapped in a computation. This not recommended setting, but it serves as an escape hatch who know what they are doing. You need to be careful not make unnecessary dependencies.


3 years ago

Major Breaking Changes

This release removes several features from the plugin by taking a fundamental architecture switch. Overall this is a huge simplification and code reduction.

Removing Control Flow

Instead of hard compiling Control Flows, the library is putting the work on Components in userland. This is infinitely more flexible and opens up different solutions to fit the library that uses it. It meant beefing up the core reconciler to be able to handle pretty much whatever gets thrown at it.

Fragments as Arrays

Document Fragments have this pesky characteristic of losing their child nodes. Using arrays is a must if you wish to retain references and pass them around. Which is necessary change if control flow is outside of the main library. This reduces the Node creation in many places, but it also means multi-nested fragments end up getting reconciled all at the same time on update. So there are some performance characteristic changes.

insert at entry

With these changes, the returned code from JSX is no longer guaranteed to be a DOM node. It may be an array or a function. In so if you use top-level child components or fragments your mount method should use insert to ensure different inputs are handled properly.

props.children changes

Now when you pass a single child you get a single value and multi-children are represented as arrays. This is consistent with how React handles children and allows a lot of different capacities for Component design. JSX element children by default are handled as dynamic and are lazily evaluated. Allowing for more powerful templating with Components.

Removing Custom Directives

Also, custom directives have been removed. They were a confusing alternative way to do things and did not play well with Typescript. Using forwardRef can achieve the same thing. The naming is under consideration and whether the library should be concerned with multiple instances on the same element (technically it works).

Better TypeScript support

Removal of Control Flow, Custom Directives, and consistent handling of JSX Children all improve TypeScript support.

Wide Open Future

While on the surface many of these changes may seem like detractors this widely opens up the ecosystem. Components or custom methods can benefit from all the performance capable here. The reactive system feeds into the renderer instead of being so closely entwined. It makes custom logic much simpler to write in the libraries without having to worry about markers and node ranges.

Look forward to updates in related Libraries over the coming weeks.


3 years ago
  • Add JSX Type Defs
  • Reduce need for dynamic markers (extra comment nodes) in many scenarios
  • Optimize local static components


4 years ago

This release is to support adding a Context API to the Fine Grained libraries. This allows for a mechanism to use the provide/inject method of hierarchical dependency injection. This also means Suspense can use this mechanism internally to simplify the management of suspended state.

Other improvements include a reduction of comment placeholder nodes. There are smarter heuristics now to generate less comment placeholders which both reduces the overhead of template cloning when instantiating the DOM nodes but also reduces the DOM node traversal when setting up the bindings.


4 years ago

Updates for handling Children of JSX Function Components:

Instead of props.children always being an array it will attempt to force it to a single value. Either the child will be a single JSX Expression whose value is returned or it if is anything else all children will be wrapped in a document fragment to be inserted. This allows but the support of things like render props while also optimizing the rendering of multi DOM children.

    <div />
    <div />
    <div />

// previously would render
const _tmpl$ = document.createElement("template"),
    _tmpl$2 = document.createElement("template"),
    _tmpl$3 = document.createElement("template");

_tmpl$.innerHTML = "<div></div>";
_tmpl$2.innerHTML = "<div></div>";
_tmpl$3.innerHTML = "<div></div>";

const template = Comp({
    children: [

// now renders
const _tmpl$ = document.createElement("template");

_tmpl$.innerHTML = "<div></div><div></div><div></div>";

const template = Comp({
    children: _tmpl$.content.cloneNode(true)

Also there is now support for dynamic child expressions which automatically wrap children access in a trackable getter:

<Comp>{( dynamicValue )}</Comp>

// now you can access it directly instead of as a function
const Comp = (prop) => <span>{( prop.children )}</span>

If you want to avoid this behavior(and the slight overhead of the getter) you can still pass functions instead of using Dynamic Properties but this API gives Component syntax parity with DOM Elements.


4 years ago

Big updates to Dom Expressions to support Tree Shaking means updates to the compiled code. Method detection to only import parts of the runtime that are used can reduce bundled code size.

Cleaned up template generation. Now in order of appearance and much more organized.


4 years ago

Several fixes:

  • Support multi-class classList keys, to support CSS in JS
  • Fix classList and style bindings to also work on non-dynamic expressions
  • Fix support for JSX comments (otherwise empty expressions are removed from the output)
  • Fix too aggressive trimming of whitespace in text interpolation