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

wrapped-web-components

v0.2.2

Published

A library that simplifies web component creation with plain HTML, JS, and CSS

Downloads

4

Readme

A lightweight JavaScript library that simplifies web component creation by allowing developers to use plain HTML, JS, and CSS instead of dealing with template.innerHTML and other low-level Web Component APIs.

New Feature: Now supports defining components in separate HTML files with native HTML, CSS, and JS syntax!

Why Wrapped Web Components?

While modern web frameworks (e.g. React, Vue, Angular) excel for complex applications, they're often overkill for simpler projects or when you just need a few interactive components on a page. jQuery-template and other lightweight alternatives (e.g. htmx) exist, but they often lack proper component encapsulation and can become difficult to manage as your project grows.

Moreover, large frameworks are UNABLE to be included in a script tag, which means you need to set up a build process, package manager, and other dependencies just to get started. This can be overwhelming for small projects or for developers who prefer a more straightforward approach.

Additionally, while Web Components provide a native way to create reusable components, they can be cumbersome to work with. Most importantly, you need to write a lot of boilerplate code to define a component, including setting up the template, shadowRoot, and other low-level APIs. This can be tedious and error-prone, especially for developers who are new to Web Components.

Svelte did a great job of solving this problem by providing a simple syntax for defining components; Google Lit also did a great job of creating Web Components using JSX; but they still requires a build step and a package manager. This means you can't just include it in your HTML file and start using it right away.

Here comes Wrapped Web Components (WWC). WWC is a lightweight library that allows you to create Web Components using NATIVE HTML, CSS, and JavaScript. You just need to register the components in a script tag, and put the component files in the same directory as your HTML file. There is nearly no learning curve to write a component. They are just like a regular HTML (or Svelte if you are familiar with it) file, but with a few extra features.

Features

  • 🚀 No Need to Compile - Use directly in your HTML without any build steps or compilation process.
  • 📦 No Need to npm Install - Load directly from CDN or include the script file - no package manager required (though you can still use npm if you prefer).
  • 📄 Separated Component Files - Define components in separate HTML files with native syntax for better organization and maintainability.
  • 🌟 Simple Hooks - Intuitive lifecycle hooks: init, connected, disconnected, attributeChanged, and propertyChanged.
  • 🧩 Light DOM by Default - Uses Light DOM by default for better CSS integration, with option to enable Shadow DOM when encapsulation is needed.
  • 🖌️ CDN Friendly - Load components from a CDN without any additional setup. Just include the script tag and you're good to go!

Installation

NPM (only if you prefer to host WWC)

npm install wrapped-web-components

CDN

The easiest way to use the library is directly from a CDN:

UMD Build (Global Variable)

<script src="https://unpkg.com/wrapped-web-components/dist/index.min.js"></script>
<script>
  // The library is available as a global variable
  const { define, html, css, register } = WebComponentWrapper;
</script>

ES Module

<script type="module">
  import { define, html, css, register } from 'https://unpkg.com/wrapped-web-components/dist/index.esm.min.js';
</script>

Alternative:

  • https://cdn.jsdelivr.net/npm/wrapped-web-components/dist/index.esm.min.js
  • https://cdn.jsdelivr.net/npm/wrapped-web-components/dist/index.min.js

Can be useful if unpkg is down or not updated, also recommended

Usage

Method 1: Using Native HTML Files (Recommended)

Create a component in a separate HTML file with native HTML, CSS, and JS:

my-counter.html

<template>
  <div class="counter">
    <h3>Counter: <span id="count-value">0</span></h3>
    <button id="increment-btn">Increment</button>
    <button id="decrement-btn">Decrement</button>
  </div>
</template>

<style>
  .counter {
    padding: 15px;
    background-color: #f0f0f0;
    border-radius: 4px;
  }

  button {
    margin-right: 5px;
    padding: 5px 10px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
</style>

<script>
  // Export component configuration
  exportDefault({
    // Properties
    properties: {
      count: {
        default: 0,
        reflect: true
      }
    },

    // Methods
    methods: {
      increment() {
        this.count++;
        this.updateCounter();
      },

      decrement() {
        this.count--;
        this.updateCounter();
      },
      
      updateCounter() {
        const countElement = this.getElement('#count-value');
        if (countElement) {
          countElement.textContent = this.count;
        }
      }
    },

    // Lifecycle hooks
    init() {
      // Add event listeners
      const incrementBtn = this.getElement('#increment-btn');
      const decrementBtn = this.getElement('#decrement-btn');

      if (incrementBtn) {
        incrementBtn.addEventListener('click', () => this.increment());
      }

      if (decrementBtn) {
        decrementBtn.addEventListener('click', () => this.decrement());
      }

      // Initialize counter display
      this.updateCounter();
    }
  });
</script>

Then register the component:

import { register } from 'wrapped-web-components';

// Register component from HTML file
register('my-counter', './components/my-counter.html');

Method 2: Using JavaScript API

import { define, html, css } from 'wrapped-web-components';

define('my-counter', {
  // HTML template with interpolation
  template: html`
    <div class="counter">
      <h3>Counter: <span>${'count'}</span></h3>
      <button ${html.event('click', 'increment')}>Increment</button>
      <button ${html.event('click', 'decrement')}>Decrement</button>
    </div>
  `,

  // CSS styles
  styles: css`
    .counter {
      padding: 15px;
      background-color: #f0f0f0;
      border-radius: 4px;
    }

    button {
      margin-right: 5px;
      padding: 5px 10px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
  `,

  // Component properties
  properties: {
    count: {
      default: 0,
      reflect: true // Reflect to attribute
    }
  },

  // Component methods
  methods: {
    increment() {
      this.count++;
      this.update();
    },

    decrement() {
      this.count--;
      this.update();
    },

    // Lifecycle hooks
    init() {
      console.log('Component initialized');
    },

    connected() {
      console.log('Component connected to DOM');
    },

    disconnected() {
      console.log('Component disconnected from DOM');
    },

    propertyChanged(name, oldValue, newValue) {
      console.log(`Property ${name} changed from ${oldValue} to ${newValue}`);
    }
  }
});

Component with Shadow DOM

define('shadow-dom-component', {
  template: html`
    <div class="container">
      <h3>Shadow DOM Component</h3>
      <p>This component uses Shadow DOM for style encapsulation.</p>
      <button ${html.event('click', 'handleClick')}>Click me</button>
    </div>
  `,

  styles: css`
    .container {
      padding: 15px;
      background-color: #e6f7ff;
      border: 1px solid #91d5ff;
    }
  `,

  methods: {
    handleClick() {
      alert('Button clicked!');
    }
  }
});

API Reference

Native HTML Components API

register(tagName, urlOrOptions)

Registers a new custom element from an HTML file or options object.

  • tagName (string): The tag name for the custom element
  • urlOrOptions (string|object): URL to an HTML file or configuration options
// Register from HTML file
register('my-counter', './components/my-counter.html');

// Register from options object
register('my-counter', { template, style, script });

HTML Component File Structure

<template>
  <!-- HTML template goes here -->
</template>

<style>
  /* CSS styles go here */
</style>

<script>
  // Export component configuration
  exportDefault({
    // Component configuration
    useShadow: true, // Optional: only needed if you want Shadow DOM (Light DOM is default)
    properties: { /* ... */ },
    methods: { /* ... */ },

    // Lifecycle hooks
    init() { /* ... */ },
    connected() { /* ... */ },
    disconnected() { /* ... */ }
  });
</script>

JavaScript API

define(tagName, options)

Defines a new custom element.

  • tagName (string): The tag name for the custom element
  • options (object): Configuration options for the component
    • template (string|function): HTML template for the component
    • styles (string): CSS styles for the component
    • useShadow (boolean): Whether to use Shadow DOM (default: false)
    • observedAttributes (array): Attributes to observe for changes
    • properties (object): Component properties with configuration
    • methods (object): Methods to add to the component

html Template Tag

Creates an HTML template string with interpolation.

html`<div>${'propertyName'}</div>`

html.event(eventName, handlerName)

Creates an event binding for use in templates.

html`<button ${html.event('click', 'handleClick')}>Click me</button>`

css Template Tag

Creates a CSS string from template literals.

css`
  .container {
    color: ${themeColor};
  }
`

Lifecycle Hooks

  • init(): Called when the component is initialized
  • connected(): Called when the component is connected to the DOM
  • disconnected(): Called when the component is disconnected from the DOM
  • propertyChanged(name, oldValue, newValue): Called when a property changes
  • attributeChanged(name, oldValue, newValue): Called when an observed attribute changes

Core Helper Methods

The library provides built-in helper methods that work with both Shadow DOM and Light DOM:

  • getElement(selector): Queries a single element using the provided selector
  • getAllElements(selector): Queries all elements matching the provided selector

Example usage:

// Works with both Shadow DOM and Light DOM
const button = this.getElement('#my-button');
const items = this.getAllElements('.item');

License

Wrapped Web Components (WWC) is released under the MIT License.


Start building with WWC today! Check out the demo for more examples and documentation.