Skip to content
Luke-zhang-04 edited this page Apr 7, 2024 · 29 revisions

DeStagnate

License npm version tests bundle size

A lightweight (~ 1 KB gzipped) wrapper around vanilla DOM methods for declarative DOM creation.

Why not just use React?

This isn't meant to be React. React has virtual DOM, hooks, and a huge ecosystem surrounding it. React 19 is supposed to have a compiler too. This is just a wrapper for when you need to create some DOM declaratively, and don't need all of React. I suppose you could use Preact, but even that involves some VDOM stuff.

Documentation

Documentation can be found at https://luke-zhang-04.github.io/DeStagnate/ for the latest version

Installation

Through NPM

# NPM
npm i destagnate --save

# Yarn
yarn add destagnate

# PNPM
pnpm i destagnate

Through curl or wget to download a bundle for browser usage

# Production
curl -L https://unpkg.com/destagnate@<VERSION_NAME>/dist/<FORMAT>/deStagnate.min.js > deStagnate.js
wget https://unpkg.com/destagnate@<VERSION_NAME>/dist/<FORMAT>/deStagnate.min.js

# Development
curl -L https://unpkg.com/destagnate@<VERSION_NAME>/dist/<FORMAT>/deStagnate.js > deStagnate.min.js
wget https://unpkg.com/destagnate@<VERSION_NAME>/dist/<FORMAT>/deStagnate.js

# Latest IIFE bundle
curl -L https://unpkg.com/[email protected]/dist/iife/deStagnate.min.js > deStagnate.min.js
wget https://unpkg.com/[email protected]/dist/iife/deStagnate.min.js

With a CDN

<!-- Production -->
<script src="https://unpkg.com/destagnate@version/dist/iife/deStagnate.min.js"></script>

<!-- Development -->
<script src="https://unpkg.com/destagnate@version/dist/iife/deStagnate.js"></script>

<!-- Latest -->
<script src="https://unpkg.com/[email protected]/dist/iife/deStagnate.min.js"></script>

Kitchen Sink Example

Sorta. There's way more you can do with DeStagnate. See https://luke-zhang-04.github.io/DeStagnate for example code and documentation.

const divRef = DeStagnate.createRef<HTMLDivElement>()

document.getElementById("my-container")?.appendChild(
    // `createElement` can be abbreviated to `ce`
    DeStagnate.createElement(
        DeStagnate.Fragment,
        null,
        DeStagnate.createElement(
            "div",
            {
                class: "my-class",
                ref: divRef,
                onMyCustomEvent: (event: Event) => console.log(event),
            },
            DeStagnate.createElement(
                "p",
                null,
                "My paragraph",
                DeStagnate.createElement("i", null, " italic"),
            ),
        ),
        DeStagnate.createElement(
            "button",
            {
                onClick: (event) =>
                    divRef.current?.dispatchEvent(
                        new CustomEvent("mycustomevent", {detail: event}),
                    ),
            },
            "Click me!",
        ),
    ),
)

// Alternatively, you can use JSX. You will need a tranpiler, though.
document.getElementById("my-container")?.appendChild(
    <>
        <div class="my-class" ref={divRef} onMyCustomEvent={(event: Event) => console.log(event)}>
            <p>
                My paragraph
                <i> italic</i>
            </p>
        </div>
        <button
            onClick={(event) =>
                divRef.current?.dispatchEvent(new CustomEvent("mycustomevent", {detail: event}))
            }
        >
            Click me!
        </button>
    </>,
)

// Using vanilla DOM methods:
const container = document.getElementById("my-container")
const div = document.createElement("div")

div.classList.add("my-class")
div.addEventListener("mycustomevent", (event) => console.log(event))

const paragraph = document.createElement("p")

paragraph.innerText = "My paragraph"

const italic = document.createElement("i")

italic.innerText = " italic"

paragraph.appendChild(italic)
div.appendChild(paragraph)
container?.appendChild(div)

const button = document.createElement("button")

button.addEventListener("click", (event) =>
    div.dispatchEvent(new CustomEvent("mycustomevent", {detail: event})),
)
button.innerText = "Click me!"

container?.appendChild(button)

Alternatives

  • What about HTL?
    • HTL is cool, but it involves an HTML parser at runtime, which makes it quite slow. You also lose devtool support whren working with strings. One upside though, you don't need to transpile to create DOM using XML-like syntax.
  • What about VHTML?
    • VHTML generates strings which you can safely add to the DOM with innerHTML. Unfortunately this has the drawback of not supporting refs or event listeners, which is extremely limiting.
  • Why not just innerHTML?
    • You're missing dev tool support, it's a big security risk, and you'll have to deal with character escaping. Not fun. Mike Bostock goes over why innerHTML is bad in the HTL README.

Using HTM

Since HTM is meant to be a drop-in replacement wherever JSX is used, DeStagnate is compatible with HTM. See using-jsx.

Using JSX

If you're using JSX, you'll need a transpiler. Either TypeScript, or a Babel with a Plugin will work.

Typescript

You can compile with this tsconfig.json

{
    "compilerOptions": {
        "jsx": "react",
        "jsxFactory": "DeStagnate.createElement",
        "jsxFragmentFactory": "DeStagnate.Fragment"
    }
}

You can also compile with this .babelrc.json

{
    "plugins": [
        [
            "@babel/plugin-transform-react-jsx",
            {
                "pragma": "DeStagnate.createElement",
                "pragmaFrag": "DeStagnate.Fragment"
            }
        ]
    ]
}
Clone this wiki locally