This minimal, zero-dependency JSX factory allows you to use React-like design patterns with web components and other native DOM elements. Most JSX patterns and special attributes are supported.
import * as React from '@turtlemay/jsx-dom'
// Define a custom element type.
class MyCustomElement extends HTMLElement {
connectedCallback() {
this.innerHTML = ''
this.appendChild(<p>hello {MyCustomElement.name}</p>)
}
}
// Register our custom element.
customElements.define(`x-${MyCustomElement.name.toLowerCase()}`, MyCustomElement)
// Use the custom element type like a React component.
document.body.appendChild(
<MyCustomElement
// Use the ref callback to save your element reference.
ref={v => this._myElemRef = v}
// Props matching a native property name will be assigned to that property.
onclick={e => {}}
// Camel cased event callbacks are also assigned via the corresponding native property.
onMouseDown={e => {}}
// Style objects are also supported.
style={{ border: '1px solid red' }}
// Anything else becomes a native attribute.
my-string-attrib="foo"
my-number-attrib={0}
my-boolean-attrib={true}
// Objects become json-stringified.
my-object-attrib={{ foo: "bar" }} />
)
npm install @turtlemay/jsx-dom
Simply import the module into your JSX files using the React
namespace:
import * as React from '@turtlemay/jsx-dom'
And write some JSX:
const attribs = {
foo: 'foo',
bar: 'bar',
}
const div = <div {...attribs} baz="baz" qux="qux" />
JSX elements become native DOM elements:
console.assert(
<div /> instanceof HTMLDivElement
)
And fragments become document fragments:
console.assert(
<></> instanceof DocumentFragment
)
With TypeScript you can use a namespace other than React
by setting your tsconfig.json
:
{
"jsx": "react",
"reactNamespace": "JSXFactory"
}
import * as JSXFactory from '@turtlemay/jsx-dom'
const div = <div />
Use a decorator for easy component registration:
// Decorator with optional tag name to define custom elements.
function registerComponent(tagName) {
return type => {
if (!tagName) tagName = 'x-' + type.name.toLowerCase()
if (customElements.get(tagName)) return
customElements.define(tagName, type)
}
}
// Decorate your components.
@registerComponent()
class MyComponent extends HTMLElement {}
For passing non-serializable props, use an initializer:
@registerComponent()
class MyComponent extends HTMLElement {
init(myArg) {
// Use your arguments to perform initialization.
return this
}
}
document.body.appendChild(
MyComponent.prototype.init.call(
<MyComponent my-serializable-prop="" />,
{ myNonSerializableProp: Symbol() }
)
)