jsx-for-web-components · npm 1.2.1 · work in progress

Components in TSX. Work in progress.

A small JSX factory and a component base class, so a custom element's markup can be written as JSX and compiled to plain DOM calls. Its README labels it WIP and this page keeps that label.

Quoted from slogsdon/jsx-for-web-components at commit 52c34a8 (2026-05-08, tag v1.2.1 from 2018-12-29). Not built, tested, or bundled while writing this page.

read this first

The README's own summary is “WIP: A basic JSX factory for use in projects leveraging web components”. The last commit that changes code is the v1.2.1 tag, 2018-12-29; the newest commit, 2026-05-08, only adds a homepage link. The package is still on npm at 1.2.1, and the repository carries five Jest test files, but the toolchain it pins is from the same period — Jest 23, ts-jest 23, TSLint 5, TypeScript 3.2.

Treat it as a working sketch of an idea, not as a maintained framework. Nothing on this page has been compiled or run; the commands and files below are quoted from the README so you can judge the idea from its own example.

the surface

h(type, props, ...children)
The pragma function the compiler calls for every JSX element. It returns a plain object — { children, props, type } — and does no DOM work itself.
JSXCustomElement
Extends HTMLElement. A subclass sets static elementName, overrides render(), and calls the inherited static register(). The constructor builds a template from render() and calls attachShadow({ mode: 'open' }); connectedCallback() clones the template into the shadow root.
createElement(node) / createTemplate(node)
Turn that object tree into DOM. node.type is either a tag name or a function: tags become document.createElement(), functions are constructed with Reflect.construct() and rendered — the StatelessComponent path. A comment in src/types.ts records the gap: “TODO: Support classes for components”.
renderToStaticMarkup(root)
Serialises an element to an HTML string: its tag, its attributes, then its shadow children, then its light children. It writes both sets inline; it does not emit a declarative shadow root.

src/register.ts mirrors the hydration library's helper: reject when elementName is unset or window.customElements is missing, otherwise define once and return customElements.whenDefined(). The repository also vendors Polymer's Custom Elements v1 Reflect shim as src/native-shim.ts, BSD licensed, with its original header intact.

install

The README's requirement is a single line: a compiler that can turn JSX into factory calls. Its install line, quoted:

yarn add jsx-for-web-components

Its worked example builds for the browser with TypeScript and Rollup, so it also installs the toolchain:

yarn add --dev typescript rollup rollup-plugin-node-resolve rollup-plugin-typescript2

The package itself declares one runtime dependency, csstype ^2.6.0, for the CSS property types in its JSX attribute definitions. It publishes lib/index.js and types/index.d.ts; npm run build is tsc, and the repository's own tsconfig.json compiles src/**/* to lib/ with strict on and DOM plus ESNext libs.

Note what that tsconfig.json does not set: jsx and jsxFactory. Those belong to the project consuming the library, and the README's example supplies them itself.

one example, quoted

The README's walkthrough, in three files. First the two compiler options that make the factory work — classic JSX, with h as the factory:

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "h",
    "moduleResolution": "node",
    "target": "es2015"
  }
}

Then the component, in src/index.tsx. The h import is unused in the source but is what the compiled output calls:

import { h, JSXCustomElement } from "jsx-for-web-components";

class ExampleElement extends JSXCustomElement {
  static elementName = "example-element";

  public render() {
    return <div id="testing">hello</div>;
  }
}

ExampleElement.register();

And the page that uses it, in index.html:

<!DOCTYPE html>
<script src="/dist/index.js" defer></script>
<example-element></example-element>

After rollup -c rollup.config.js — the README also prints the Rollup config it expects, an IIFE bundle of ./src/index.tsx into ./dist/index.js — the README says the page should load in a browser and show the rendered work. That claim is the README's, not this site's: it was not reproduced here.

how it differs

The runtime pattern is the one the other two tools use, minus the server: a template is filled from a render result, an open shadow root is attached in the constructor, and the clone is appended on connect. There is no naming convention shared with hydration — no template- id lookup and no data-template placeholder — because this library renders its own elements directly rather than adopting server markup.

Its renderToStaticMarkup() suggests a server-rendering path, but it serialises an element's shadow children into the element's own light DOM. If you need server output that a browser renders before JavaScript runs, nothing here produces it: no file in this repository emits <template shadowrootmode>.

If you need a super call, the README's note is explicit: override connectedCallback() and call super.connectedCallback(), or the template never gets cloned into the shadow root.

where to go