npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webjsx

v0.0.73

Published

A minimal library for building web applications with JSX and Web Components. It focuses on simplicity, providing just **two core functions**:

Readme

WebJSX

A minimal library for building web applications with JSX and Web Components. It focuses on simplicity, providing just two core functions:

  • createElement: Creates virtual DOM elements using JSX.
  • applyDiff: Efficiently applies changes to the real DOM by comparing virtual nodes.

Examples

There are a few examples on StackBlitz. If you're impatient (like me), that's probably the easiest way to get started.

Installation

Install webjsx via npm:

npm install webjsx

Getting Started

WebJSX fully supports JSX syntax, allowing you to create virtual DOM elements using createElement and update the real DOM with applyDiff.

import * as webjsx from "webjsx";

// Define a simple virtual DOM element using JSX
const vdom = (
  <div id="main-container">
    <h1>Welcome to webjsx</h1>
    <p>This is a simple example.</p>
  </div>
);

// Select the container in the real DOM
const appContainer = document.getElementById("app");

// Apply the virtual DOM diff to update the real DOM
webjsx.applyDiff(appContainer, vdom);

Defining and Using Web Components with JSX

Let's write a simple Custom Element with JSX.

import * as webjsx from "webjsx";

// Define a custom Web Component
class MyElement extends HTMLElement {
  static get observedAttributes() {
    return ["title", "count"];
  }

  constructor() {
    super();
    this._count = 0;
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === "title" || name === "count") {
      this.render();
    }
  }

  set count(val) {
    this._count = val;
    this.render();
  }

  get count() {
    return this._count;
  }

  render() {
    // Use webjsx's applyDiff to render JSX inside the Web Component
    const vdom = (
      <div>
        <h2>{this.getAttribute("title")}</h2>
        <p>Count: {this.count}</p>
      </div>
    );
    webjsx.applyDiff(this, vdom);
  }
}

// Register the custom element
if (!customElements.get("my-element")) {
  customElements.define("my-element", MyElement);
}

// Create a virtual DOM with the custom Web Component
const vdom = <my-element title="Initial Title" count={10}></my-element>;

// Render the custom Web Component
const appContainer = document.getElementById("app");
webjsx.applyDiff(appContainer, vdom);

Handling Events in JSX

Attach event listeners directly within your JSX using standard HTML event attributes.

import * as webjsx from "webjsx";

// Define an event handler
const handleClick = () => {
  alert("Button clicked!");
};

// Create a button with an onclick event
const vdom = <button onclick={handleClick}>Click Me</button>;

// Render the button
const appContainer = document.getElementById("app");
webjsx.applyDiff(appContainer, vdom);

Using Fragments

Group multiple elements without introducing additional nodes to the DOM using <>...</> syntax.

import * as webjsx from "webjsx";

// Define a custom Web Component using fragments
class MyList extends HTMLElement {
  connectedCallback() {
    const vdom = (
      <>
        <h2>My List</h2>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
          <li>Item 3</li>
        </ul>
        <footer>Total items: 3</footer>
      </>
    );
    webjsx.applyDiff(this, vdom);
  }
}

// Register the custom element
if (!customElements.get("my-list")) {
  customElements.define("my-list", MyList);
}

// Render the custom Web Component
const appContainer = document.getElementById("app");
const vdom = <my-list></my-list>;
webjsx.applyDiff(appContainer, vdom);

API Reference

createElement(tag, props, children)

Creates a virtual DOM element.

JSX calls createElement implicitly:

const vdom = (
  <div id="main-container">
    <h1>Welcome to webjsx</h1>
  </div>
);

Usage (Non-JSX):

const vdom = webjsx.createElement(
  "div",
  { id: "main-container" },
  webjsx.createElement("h1", null, "Welcome to webjsx")
);

applyDiff(parent, newVirtualNode)

Applies the differences between the new virtual node(s) and the existing DOM.

Usage:

const vdom = <p class="text">Updated Text</p>;
webjsx.applyDiff(appContainer, vdom);

Fragment

A special type used to group multiple elements without adding extra nodes to the DOM.

Usage:

<>
  <span>Item 1</span>
  <span>Item 2</span>
</>

createDOMElement(vElement)

You probably won't need to use this directly. But if you want to convert a virtual DOM Element into a real DOM Element you can use createDOMElement.

Usage:

const vnode = <div>Hello, world!</div>;
const domNode = webjsx.createDOMElement(vnode);
document.body.appendChild(domNode);

Example: Creating a Counter Web Component

import * as webjsx from "webjsx";

// Define the custom Web Component
class CounterElement extends HTMLElement {
  static get observedAttributes() {
    return ["title", "count"];
  }

  constructor() {
    super();
    this._count = 0;
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === "title" || name === "count") {
      this.render();
    }
  }

  set count(val) {
    this._count = val;
    this.render();
  }

  get count() {
    return this._count;
  }

  render() {
    // Render JSX inside the Web Component
    const vdom = (
      <div>
        <h2>{this.getAttribute("title")}</h2>
        <p>Count: {this.count}</p>
        <button onclick={this.increment.bind(this)}>Increment</button>
      </div>
    );
    webjsx.applyDiff(this, vdom);
  }

  increment() {
    this.count += 1;
  }
}

// Register the custom element
if (!customElements.get("counter-element")) {
  customElements.define("counter-element", CounterElement);
}

// Create and render the CounterElement
const vdom = <counter-element title="My Counter" count={0}></counter-element>;

const appContainer = document.getElementById("app");
webjsx.applyDiff(appContainer, vdom);

Advanced: Rendering Suspension

If a class defines the webjsx_suspendRendering and webjsx_resumeRendering methods, WebJSX will call the former before setting properties and the latter after all properties are set. This allows you to suspend rendering while multiple properties are being set, which would otherwise result in multiple re-renders.

In the following example, for the JSX markup , the render() method is called only once after both properties are set:

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.renderingSuspended = false;
  }

  render() {
    if (!this.renderingSuspended) {
      this.textContent = `Prop1: ${this.getAttribute(
        "prop1"
      )}, Prop2: ${this.getAttribute("prop2")}`;
    }
  }

  __webjsx_suspendRendering() {
    this.renderingSuspended = true;
  }

  __webjsx_resumeRendering() {
    this.renderingSuspended = false;
    this.render(); // Perform the actual rendering
  }
}

TypeScript

tsconfig.json

Ensure your tsconfig.json is set up to handle JSX.

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "webjsx"
  }
}

Adding Custom Elements to IntrinsicElements (TypeScript)

TypeScript will complain that your Custom Element (such as <counter-element>) is not found. That's because it is only aware of standard HTML elements and doesn't know what <counter-element> is.

To fix this you need to declare custom elements in a declarations file, such as custom-elements.d.ts:

import "webjsx";

declare global {
  namespace JSX {
    interface IntrinsicElements {
      "counter-element": {
        count: number;
      };
      "sidebar-component": {
        about: string;
        email: string;
      };
    }
  }
}

Bundling

You can bundle with your favorite bundler, but most apps don't need to.

You can load modules directly on the web page these days:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>WebJsx Test</title>

    <!-- node_modules or wherever you downloaded webjsx -->
    <script type="importmap">
      {
        "imports": {
          "webjsx": "../node_modules/webjsx/dist/index.js",
          "webjsx/jsx-runtime": "../node_modules/webjsx/dist/jsx-runtime.js"
        }
      }
    </script>
    <!-- This is your entry point -->
    <script type="module" src="../dist/index.js"></script>
  </head>

  <body>
    <div id="app"></div>
  </body>
</html>

You can see more examples on StackBlitz.

Routing

For routing needs, you can use webjsx-router, a minimal type-safe pattern matching router designed specifically for WebJSX applications.

Installation

npm install webjsx-router

Basic Routing Example

import * as webjsx from "webjsx";
import { match, goto, initRouter } from "webjsx-router";

// Initialize router with routing logic
const container = document.getElementById("app")!;
initRouter(
  container,
  () =>
    match("/users/:id", (params) => <user-details id={params.id} />) ||
    match("/users", () => <user-list />) ||
    <not-found />
);

// Navigation with goto
goto("/users/123");

// With query parameters
goto("/search", { q: "test", sort: "desc" });

URL Pattern Examples

// Static routes
match("/about", () => <about-page />);

// Routes with parameters
match("/users/:id", (params) => <user-details id={params.id} />);

// Query parameters
// URL: /search?q=test&sort=desc
match("/search", (params, query) => (
  <search-results query={query.q} sort={query.sort} />
));

The router includes TypeScript support with automatic type inference for parameters and query strings. For more details, check out the webjsx-router documentation.

Contributing

Contributions are welcome! Whether it's reporting bugs, suggesting features, or submitting pull requests, your help is appreciated. Please ensure that your contributions adhere to the project's coding standards and include appropriate tests.

To run the tests:

npm test

License

WebJSX is open-source software licensed as MIT.

Support

If you encounter any issues or have questions, feel free to open an issue on GitHub or reach out via Twitter @jeswin.