# only-jsx

> JSX runtime for Vanilla JS

Latest version **0.1.4** (published 2025-09-15) · MIT license · 0 weekly downloads

## Install

```sh
npm install only-jsx
pnpm add only-jsx
yarn add only-jsx
bun add only-jsx
```

## Health

**Score 60/100 (C)** — status: stable.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.1.4 |
| Published | 2025-09-15 |
| First published | 2022-11-03 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 25.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 4 |
| Author | only-jsx |
| Maintainers | only-jsx |
| Keywords | JSX, Vanilla JS |

## Links

- npm: https://www.npmjs.com/package/only-jsx
- Repository: https://github.com/only-jsx/jsx-runtime
- Homepage: https://github.com/only-jsx/jsx-runtime#readme
- Issues: https://github.com/only-jsx/jsx-runtime/issues
- npm.io page: https://npm.io/package/only-jsx

## Recent versions

- 0.1.4 (latest) — 2025-09-15
- 0.1.3 — 2023-09-10
- 0.1.2 — 2023-08-12
- 0.1.1 — 2023-01-11
- 0.1.0 — 2023-01-08
- 0.0.3-beta.10 — 2022-12-20
- 0.0.3-beta.9 — 2022-12-11
- 0.0.3-beta.8 — 2022-12-11
- 0.0.3-beta.7 — 2022-12-10
- 0.0.3-beta.6 — 2022-12-10
- 0.0.3-beta.5 — 2022-12-10
- 0.0.3-beta.4 — 2022-12-10
- 0.0.3-beta.3 — 2022-12-10
- 0.0.3-beta.2 — 2022-12-10
- 0.0.3-beta.1 — 2022-12-10
- … 16 more at https://npm.io/package/only-jsx/versions

## README

# only-jsx
Another JSX runtime for Vanilla JS without react, vue, or something else

## install and config

```bash
npm i only-jsx
```

and edit `babel.config.js` or `.babelrc` or other babel config:

```
...
    plugins: [
        ...
        [
            '@babel/plugin-transform-react-jsx',
            {
                'runtime': 'automatic',
                'importSource': 'only-jsx'
            }
        ]
    ]
...
```

or `tsconfig.json`

```
...
    "compilerOptions": {
        ...
        "jsx": "react-jsx",
        "jsxImportSource": "only-jsx",
    }
...
```

then coding js or ts with jsx.

## context

Some JSX elements require context from their parents to be rendered correctly. Examples are children of an SVG element that require a namespaceURI from the parent.
The setContext method is designed to resolve this issue.

```tsx
const SvgContext = (props: OptionsChildren, ctx: any) => (props.children as ContextFunc)(ctx);

export default (props: OptionsChildren) => {
    setContext(SvgContext, {})
    return <SvgContext><svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'>
        <path d={props.children}></path>
    </svg></SvgContext>;
}
```
or even simpler

```jsx
export default props => {
    setContext('svg', {})
    return <svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'>
        <path d={props.children}></path>
    </svg>;
}
```

## examples

The source codes are in the repository https://github.com/only-jsx/examples.

```jsx
//index.jsx
import APP from './app';

function render() {
    const appProps = {a:1, b:2};

    function onClick() {
        appProps.a++;
        appProps.b++;
        appProps.update();
    }

    const t = <>
        Hello
        <APP props={appProps}>In <span>APP</span></APP>
        <button onclick={onClick}>Update APP</button>
    </>;

    window.onload = () => {
        const e = document.getElementById('root');
        e.replaceChildren(t);
    }
}

render();

//app.jsx
export default ({props, children}) => {
    const result = {};
    const e = <div data-custom={props.b}>
        <span>{props.a + props.b}</span>
        <button onclick={onClick}>Button</button>
        <div ref={result}></div>
        <div id='counter' style='color: blue;'></div>
        {children}
    </div>;

    let counter = 0;
    function onClick() {
        fetch('https://api.github.com/repos/only-jsx/jsx-runtime/contributors').then(data => {
            data.json().then(contributors => {
                counter++;
                const c = contributors.map(user => <div>{user.login} {user.contributions}</div>)
                result.current.replaceChildren(...c);
                e.querySelector('#counter').replaceChildren(<span>{counter} requests</span>);
            });
        });
    }

    props.update = function () {
        e.dataset.custom = this.b;
        e.firstChild.innerText = this.a + this.b;
        result.current.replaceChildren();
    }

    return e;
}
```
```tsx
//index.tsx
import APP, {AppProps} from './app';
import {Fragment} from 'only-jsx/jsx-runtime';

function render() {
    const root = document.getElementById('root');
    function replace (e: HTMLElement ) {
        root.replaceChildren(e);
    }

    const appProps: AppProps = {
        a:1,
        b:2,
        replace,
    };

    function onClick() {
        appProps.a++;
        appProps.b++;
        appProps.update?.();
    }

    const t = <Fragment>
        Hello
        <APP props={appProps}>In <span>APP</span></APP>
        <button onclick={onClick}>Increase numbers</button>
    </Fragment>;

    window.onload = () => replace(t);
}

render();

//app.tsx
import styles from './app.module.css';
import {OptionsChildren} from 'only-jsx/jsx-runtime';

interface GhContrubutor {
    login: string;
    contributions: number;
}

export interface AppProps {
    a: number;
    b: number;
    replace: (e: HTMLElement ) => void;
    update?: () => void;
}

export default ({ props, children }: { props: AppProps, children?: OptionsChildren }) => {
    const result: { current?: HTMLElement } = {};
    const e = <div data-custom={props.b}>
        <span>{props.a + props.b}</span>
        <button onclick={onClickFetch}>Fetch</button>
        <div id='counter' style='color: blue;'></div>
        <div ref={result}></div>
        {children}
        <button onclick={onClickReplace}>Replace content</button>
    </div>;

    function onClickReplace() {
        props.replace(<p>Content was completely replaced</p>);
    }

    let counter = 0;
    function onClickFetch() {
        fetch('https://api.github.com/repos/only-jsx/jsx-runtime/contributors').then(data => {
            counter++;
            data.json().then((contributors: GhContrubutor[]) => {
                const c = contributors.map(user => <div>{user.login} {user.contributions}</div>)
                result.current.replaceChildren(...c);
                result.current.className = styles.result;
            }).catch(err=>{
                result.current.replaceChildren(err.message);
                result.current.className = styles.error;
            });
            e.querySelector('#counter').replaceChildren(<span>Request #{counter}</span>);
        });
    }

    props.update = function () {
        e.dataset.custom = this.b;
        e.firstChild.innerText = this.a + this.b;
    }

    return e;
}
```

---
_Source: https://npm.io/package/only-jsx · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
