# untrue

> Render user interfaces.

Latest version **5.19.0** (published 2026-04-28) · MIT license · 0 weekly downloads

## Install

```sh
npm install untrue
pnpm add untrue
yarn add untrue
bun add untrue
```

## Health

**Score 55/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 5.19.0 |
| Published | 2026-04-28 |
| First published | 2022-11-19 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 146.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Maintainers | josecarlosrx |
| Keywords | untrue, frontend, ui |

## Links

- npm: https://www.npmjs.com/package/untrue
- Repository: https://github.com/iconshot/untrue
- Homepage: https://untrue.dev
- Issues: https://github.com/iconshot/untrue/issues
- npm.io page: https://npm.io/package/untrue

## Dependencies (1)

- [everemitter](https://npm.io/package/everemitter.md) ^1.4.1

## Alternatives

- [@sindresorhus/slugify](https://npm.io/package/@sindresorhus/slugify.md) — 3.7M weekly downloads
- [solid-js](https://npm.io/package/solid-js.md) — 2.7M weekly downloads
- [expo-glass-effect](https://npm.io/package/expo-glass-effect.md) — 2.5M weekly downloads
- [nanoassert](https://npm.io/package/nanoassert.md) — 780.8K weekly downloads
- [@ffmpeg/ffmpeg](https://npm.io/package/@ffmpeg/ffmpeg.md) — 529.5K weekly downloads

## Recent versions

- 5.19.0 (latest) — 2026-04-28
- 5.18.0 — 2025-11-07
- 5.17.2 — 2025-11-07
- 5.17.1 — 2025-11-07
- 5.17.0 — 2025-11-07
- 5.16.2 — 2025-10-17
- 5.16.1 — 2025-10-16
- 5.16.0 — 2025-10-16
- 5.15.3 — 2025-07-24
- 5.15.2 — 2025-07-22
- 5.15.1 — 2025-05-30
- 5.15.0 — 2025-02-19
- 5.14.3 — 2025-02-19
- 5.14.2 — 2025-02-19
- 5.14.1 — 2025-02-18
- … 208 more at https://npm.io/package/untrue/versions

## README

# [Untrue](https://untrue.dev/)

JavaScript library for rendering user interfaces.

## Installation

The easiest way to get started with Untrue is through a web app.

```
npm i untrue @untrue/web
```

Compatible with any build tool: [Parcel](https://parceljs.org/), [Vite](https://vitejs.dev/), [Webpack](https://webpack.js.org/), etc.

<sub>Native app development available with [Detonator](https://detonator.dev).</sub>

## Get started

You can add Untrue to any part of your page.

```ts
import $ from "untrue";

import { Tree } from "@untrue/web";

import App from "./App";

const tree = new Tree(document.body);

// $ is a shorthand to represent slots

tree.mount($(App));
```

In this case, we're adding Untrue to `body`.

More on `App` in the next section.

## Basic features

### Interactivity

A component state can change at any time and Untrue knows which nodes should be updated in the DOM.

```ts
import $, { Hook } from "untrue";

function App() {
  const [counter, updateCounter] = Hook.useState(0);

  const onIncrement = () => {
    updateCounter(counter + 1);
  };

  // regular arrays are used to return a list of slots

  // after the first click, counter is no longer 0 but 1

  return [
    $("span", counter),
    $("button", { onclick: onIncrement }, "increment"),
  ];
}

export default App;
```

The output HTML will be:

```html
<span>0</span> <button>increment</button>
```

`button` will have an `onclick` listener attached to it.

`span` will be updated with the new `counter` every time `button` is clicked.

### Modularity

Components can be classes or functions and are used to group multiple slots.

```ts
import $, { Hook, Props } from "untrue";

function App() {
  return [
    $(Header, { title: "Untrue" }), // pass title as prop (external data)
    $(Footer, { year: 2049 }), // pass year as prop (external data)
  ];
}

interface HeaderProps extends Props {
  title: string;
}

function Header({ title }: HeaderProps) {
  const [counter, updateCounter] = Hook.useState(0); // internal data

  const onIncrement = () => {
    updateCounter(counter + 1);
  };

  return $("header", [
    $("h1", title),
    $("div", [
      $("span", counter),
      $("button", { onclick: onIncrement }, "increment"),
    ]),
  ]);
}

interface FooterProps extends Props {
  year: number;
}

function Footer({ year }: FooterProps) {
  return $("footer", [
    $("span", `copyright, ${year}`),
    $("br"),
    $("a", { href: "https://example.com" }, "some anchor link"),
  ]);
}

export default App;
```

The output HTML will be:

```html
<header>
  <h1>Untrue</h1>
  <div>
    <span>0</span>
    <button>increment</button>
  </div>
</header>
<footer>
  <span>copyright, 2049</span>
  <br />
  <a href="https://example.com">some anchor link</a>
</footer>
```

`Header` has some `counter` that will be updated with `button`.

### Lifecycle events

- `mount`: The first render.
- `update`: Every render after the first one.
- `render`: Every render. It's fired after `mount` or `update` events.
- `unmount`: Component has been unmounted.

Multiple event listeners can be attached to a single event. Specially useful to have more organized code.

```ts
import $, { Hook } from "untrue";

function App() {
  const [running, updateRunning] = Hook.useState(false);

  const onClick = () => {
    updateRunning(!running);
  };

  return [
    $("button", { onclick: onClick }, running ? "end timer" : "start timer"),
    $("br"),
    running ? $(Timer) : null,
  ];
}

function Timer() {
  const [counter, updateCounter] = Hook.useState(0);

  Hook.useMountLifecycle(() => {
    console.log("Timer mounted");
  });

  Hook.useUpdateLifecycle(() => {
    console.log("Timer updated");
  });

  Hook.useEffect(() => {
    const timeout = setTimeout(() => {
      updateCounter(counter + 1);
    }, 1000);

    return () => {
      clearTimeout(timeout);
    };
  }, [counter]);

  return $("span", counter);
}

export default App;
```

After the button click, the output HTML will be:

```html
<button>end timer</button>
<br />
<span>0</span>
```

`counter` is incremented every second.

`Timer mounted` is logged first followed by `Timer updated` for updates.

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