-
Notifications
You must be signed in to change notification settings - Fork 2
Home
It's the Babel plugin to transforms JSX syntax to the DOM elements with minimal runtime dependency ~500 B. Support HTML, SVG, and MathML tags.
source code:
document.body.append(
<main class="box">
<h1 class="title">Hello, World!</h1>
</main>
);
after compilation:
import { jsx as _jsx } from "jsx-dom-runtime";
document.body.append(
_jsx("main", {
class: "box",
children: _jsx("h1", {
class: "title",
children: "Hello, World!"
})
})
);
You don't need to import any dependencies to your code. The babel will automatically import all necessary functions.
npm i jsx-dom-runtime
# or
yarn add jsx-dom-runtime
Add preset to your .babelrc
file.
.babelrc
{
"presets": [
"jsx-dom-runtime/babel-preset"
]
}
Support the general JSX syntax.
Write the attributes closer to HTML that to JavaScript
Use attribute class
instead of the className
DOM property as in React.
- <div className="box" />
+ <div class="box" />
Attribute for
instead htmlFor
DOM property
- <label htmlFor="cheese">Do you like cheese?</label>
+ <label for="cheese">Do you like cheese?</label>
The style
attribute supports the JavaScript object and a string value. Also, you can use CSS custom properties
<div style="background-color: #ffe7e8; border: 2px solid #e66465;" />
<div style="--color: red;" />
// or
<div style={{ backgroundColor: '#ffe7e8', border: '2px solid #e66465' }} />
<div style={{ '--color': 'red' }} />
Use unmodified SVG attributes instead of camelCase style as in React
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
- <circle strokeWidth="2" strokeLinejoin="round" cx="24" cy="24" r="20" fill="none" />
+ <circle stroke-width="2" stroke-linejoin="round" cx="24" cy="24" r="20" fill="none" />
</svg>
Don't use namespaced attributes. The namespaced attributes are deprecated and no longer recommended.
Instead of xlink:href
you should use href
<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg">
- <a xlink:href="https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href">
+ <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href">
<text x="10" y="25">MDN Web Docs</text>
</a>
</svg>
There are a few ways to add event handling to a DOM Element.
- Using the event handler properties that start with
on*
asonclick
orondblclick
.
<button
type="button"
onclick={(event) => { }}
ondblclick={(event) => { }}
>
Click Me!
</button>;
Be attention! In this way, the event listener will be assigned directly to the Element object as a property.
// Equivalent on vanilla JavaScript
button.onclick = (event) => { };
button.ondblclick = (event) => { };
- Using the namespace syntax for the event listener that start with
on:*
ason:change
oron:focus
.
<input
type="text"
on:change={(event) => { }}
on:focus={(event) => { }}
/>
After the compilation, it registers the event with addEventListener
// Equivalent on vanilla JavaScript
input.addEventListener('change', (event) => { });
input.addEventListener('focus', (event) => { });
- Using
ref
callback. The callback will be called with a target element when the element will be created.
<button
type="button"
ref={(node) => {
// Use capture phase
node.addEventListener('click', (event) => { }, true);
// With event options
node.addEventListener('dblclick', (event) => { }, { once: true });
}}>
Click Me!
</button>;
Function components must start with a capital letter or they won’t work.
const App = ({ name }) => (
<div>Hello {name}</div>
);
document.body.append(<App name="Bob" />);
Use <>...</>
syntax, to group multiple elements together. Under the hood, it use DocumentFragment
interface.
document.body.append(
<>
<p>Hello</p>
<p>World</p>
</>
);
Adding a reference to a DOM Element. When a ref is passed to an element in create, a reference to the node becomes accessible at the current
attribute of the ref
import { useRef } from 'jsx-dom-runtime';
const ref = useRef();
const addItem = () => {
// add an item to the list
ref.current.append(<li>New Item</li>);
};
document.body.append(
<>
<button type="button" on:click={addItem}>
Add Item
</button>
<ul ref={ref} />
</>
);
Another way to get the reference to an element can be done by passing a function callback. The callback will be called with the actual reference DOM element
const setRef = (node) => {
node.addEventListener('focusin', () => {
node.style.backgroundColor = 'pink';
});
node.addEventListener('focusout', () => {
node.style.backgroundColor = '';
});
};
document.body.append(
<input type="text" ref={setRef} />
);
Use the Text node in a DOM tree.
import { useText } from 'jsx-dom-runtime';
const [text, setText] = useText('The initial text');
const clickHandler = () => {
setText('Clicked!');
};
document.body.append(
<>
<p>{text}</p>
<button type="button" on:click={clickHandler}>
Click me
</button>
</>
);
Get template from a string.
import { Template } from 'jsx-dom-runtime';
document.body.append(
<Template>
{`<svg width="24" height="24" aria-hidden="true">
<path d="M12 12V6h-1v6H5v1h6v6h1v-6h6v-1z"/>
</svg>`}
</Template>
);
Add support of the DOM Element object properties. By default supported property value
.
import { properties } from 'jsx-dom-runtime';
properties.add('textContent') // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
.add('innerHTML') // https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
.add('volume') // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume
.add('muted'); // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/muted
document.body.append(
<>
<div textContent="Hello, world!" />
<div innerHTML="<p>Hello, world!</p>" />
<audio
src="https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3"
controls
volume={0.9}
muted
/>
</>
);
TypeScript types definition for the DOM Element properties
global.d.ts
declare global {
namespace JSX {
// add types for all JSX elements
interface Attributes {
textContent?: string;
innerHTML?: string;
}
// add types only for Audio element
interface HTMLAudioElementAttributes {
volume?: number;
muted?: boolean;
}
}
}
export {};
Add custom attributes in JSX.Element
.
import { extensions } from 'jsx-dom-runtime';
extensions
.set('x-class', (node, value) => {
node.setAttribute('class', value.filter(Boolean).join(' '));
})
.set('x-dataset', (node, value) => {
for (let key in value) {
if (value[key] != null) {
node.dataset[key] = value[key];
}
}
})
.set('x-autofocus', (node, value) => {
setTimeout(() => node.focus(), value);
});
document.body.append(
<input
x-class={['one', 'two']}
x-dataset={{ testid: 'test', hook: 'text' }}
x-autofocus={1000}
/>
);
Result
<input class="one two" data-testid="test" data-hook="text">
TypeScript types definition for custom attributes:
global.d.ts
declare global {
namespace JSX {
// add types for all JSX elements
interface Attributes {
'x-class'?: string[];
'x-dataset'?: Record<string, string>;
}
// add types only for Input elements
interface HTMLInputElementAttributes {
'x-autofocus'?: number;
}
}
}
export {};
TypeScript can be used only for type checking. The library doesn't support compilation with TypeScript. Use @babel/preset-typescript
.babelrc
{
"presets": [
"@babel/preset-typescript",
"jsx-dom-runtime/babel-preset"
]
}
To provide type checking add tsconfig.json
file to your project:
tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "jsx-dom-runtime",
"moduleResolution": "node",
"noEmit": true,
"lib": [
"DOM"
]
}
}
Example:
src/index.tsx
import { useText } from 'jsx-dom-runtime';
interface Props {
label: string;
}
const App: JSX.FC<Props> = ({ label }) => {
let i = 0;
const [textNode, setCount] = useText(i);
const clickHandler: JSX.EventListener = () => {
setCount(++i);
};
return (
<div class="card">
<h1 class="label">{label}</h1>
<button type="button" on:click={clickHandler}>
Click me! {textNode}
</button>
</div>
);
};
document.body.append(<App label="Hello!" />);