Introduction
The same <my-counter/>
Web Component has been written in 55 variants.
Table of Content
Source code and Bundle analysis
Select implementation:
HTMLElement
Homepage: https://html.spec.whatwg.org/multipage/custom-elements.html
Try in WebComponents.devconst template = document.createElement('template');
template.innerHTML = `
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button id="dec">-</button>
<span id="count"></span>
<button id="inc">+</button>`;
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.shadowRoot.getElementById('inc').onclick = () => this.inc();
this.shadowRoot.getElementById('dec').onclick = () => this.dec();
this.update(this.count);
}
inc() {
this.update(++this.count);
}
dec() {
this.update(--this.count);
}
update(count) {
this.shadowRoot.getElementById('count').innerHTML = count;
}
}
customElements.define('my-counter', MyCounter);
No dependencies
Component size including library
Minified - Uncompressed | 993 |
Minified + Gzip | 505 |
Minified + Brotli | 363 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ Alpine JS)
A rugged, minimal framework for composing JavaScript behavior in your markup.
Homepage: https://github.com/alpinejs/alpine
GitHub: https://github.com/alpinejs/alpine
Try in WebComponents.devimport "alpinejs";
const template = document.createElement("template");
template.innerHTML = `
<style>
span, button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<div x-data="$el.parentElement.data()">
<button @click="dec()">-</button>
<span x-text="count"></span>
<button @click="inc()">+</button>
</div>
`;
export class MyCounter extends HTMLElement {
connectedCallback() {
this.append(template.content.cloneNode(true));
}
data() {
return {
count: 0,
inc() {
this.count++;
},
dec() {
this.count--;
},
};
}
}
customElements.define("my-counter", MyCounter);
Dependencies
alpine :
Component size including library
Minified - Uncompressed | 27,633 |
Minified + Gzip | 8,906 |
Minified + Brotli | 7,983 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ catalyst)
Catalyst is a set of patterns and techniques for developing components within a complex application. At its core, Catalyst simply provides a small library of functions to make developing Web Components easier.
Homepage: https://github.github.io/catalyst/
GitHub: https://github.com/github/catalyst
Try in WebComponents.devimport { controller, target } from "@github/catalyst";
const template = document.createElement("template");
template.innerHTML = `<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button data-action="click:my-counter#dec">-</button>
<span data-target="my-counter.value"></span>
<button data-action="click:my-counter#inc">+</button>`;
@controller
class MyCounterElement extends HTMLElement {
@target value: HTMLElement;
count: number = 0;
connectedCallback() {
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.update();
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
update() {
this.value.textContent = "" + this.count;
}
}
Dependencies
@github/catalyst : ^1.1.3
Component size including library
Minified - Uncompressed | 4,580 |
Minified + Gzip | 1,833 |
Minified + Brotli | 1,566 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ jtml)
@github/jtml - This library is designed as a layer on top of @github/template-parts to provide declarative, JavaScript based HTML template tags.
Homepage: https://www.npmjs.com/package/@github/jtml
Try in WebComponents.devimport { html, render } from "@github/jtml";
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
}
connectedCallback() {
this.attachShadow({mode: 'open'})
this.update()
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
update() {
render(html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="${() => this.dec()}">-</button>
<span>${this.count}</span>
<button onclick="${() => this.inc()}">+</button>
`,
this.shadowRoot);
}
}
customElements.define("my-counter", MyCounter);
Dependencies
@github/jtml : ^0.4.0
Component size including library
Minified - Uncompressed | 7,066 |
Minified + Gzip | 2,444 |
Minified + Brotli | 2,139 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ Lighterhtml)
The hyperHTML strength & experience without its complexity
Homepage: https://medium.com/@WebReflection/lit-html-vs-hyperhtml-vs-lighterhtml-c084abfe1285
GitHub: https://github.com/WebReflection/lighterhtml
Try in WebComponents.devimport { html, render } from 'lighterhtml';
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.update();
}
inc = () => {
this.count++;
this.update();
};
dec = () => {
this.count--;
this.update();
};
style() {
return `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
}
template() {
return html`
<style>
${this.style()}
</style>
<button onclick="${this.dec}">-</button>
<span>${this.count}</span>
<button onclick="${this.inc}">+</button>
`;
}
update() {
render(this.shadowRoot, this.template());
}
}
customElements.define('my-counter', MyCounter);
Dependencies
lighterhtml : ^4.2.0
Component size including library
Minified - Uncompressed | 12,215 |
Minified + Gzip | 5,281 |
Minified + Brotli | 4,774 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ Lit (new))
Lit - Simple. Fast. Web Components.
Homepage: https://lit.dev
GitHub: https://github.com/lit/lit
Try in WebComponents.devimport { html, render } from 'lit';
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.update();
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
style() {
return /*css*/ `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
}
template() {
return html`
<style>
${this.style()}
</style>
<button @click="${this.dec}">-</button>
<span>${this.count}</span>
<button @click="${this.inc}">+</button>
`;
}
update() {
render(this.template(), this.shadowRoot, { eventContext: this });
}
}
customElements.define('my-counter', MyCounter);
Dependencies
lit : ^2.0.0-rc.1
Component size including library
Minified - Uncompressed | 15,369 |
Minified + Gzip | 5,671 |
Minified + Brotli | 5,111 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ Lit-html)
Lit-Html - An efficient, expressive, extensible HTML templating library for JavaScript
Homepage: https://lit-html.polymer-project.org/
GitHub: https://github.com/polymer/lit-html
Try in WebComponents.devimport { html, render } from 'lit-html';
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.update();
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
style() {
return `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
}
template() {
return html`
<style>
${this.style()}
</style>
<button @click="${this.dec}">-</button>
<span>${this.count}</span>
<button @click="${this.inc}">+</button>
`;
}
update() {
render(this.template(), this.shadowRoot, { eventContext: this });
}
}
customElements.define('my-counter', MyCounter);
Dependencies
lit-html : ^1.4.1
Component size including library
Minified - Uncompressed | 10,226 |
Minified + Gzip | 3,638 |
Minified + Brotli | 3,240 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ Mithril)
A JavaScript Framework for Building Brilliant Applications
Homepage: https://mithril.js.org/
GitHub: https://github.com/MithrilJS/mithril.js
Try in WebComponents.dev/* @jsx m */
import m from "mithril";
export class MyCounter extends HTMLElement {
count: number = 0;
constructor() {
super();
this.attachShadow({ mode: "open" });
}
get styles() {
return (
<style>{`
span,button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`}</style>
);
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
connectedCallback() {
this.update();
}
update() {
m.render(
this.shadowRoot as any,
<div>
{this.styles}
<button onclick={this.dec.bind(this)}>-</button>
<span>{this.count}</span>
<button onclick={this.inc.bind(this)}>+</button>
</div>
);
}
}
customElements.define("my-counter", MyCounter);
Dependencies
mithril : ^2.0.4
Component size including library
Minified - Uncompressed | 28,344 |
Minified + Gzip | 10,160 |
Minified + Brotli | 9,101 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HTMLElement (w/ uhtml)
micro html is a ~2.5K lighterhtml subset to build declarative and reactive UI via template literals tags.
Homepage: https://github.com/WebReflection/uhtml
GitHub: https://github.com/WebReflection/uhtml
Try in WebComponents.devimport { html, render } from "uhtml";
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.update();
}
inc = () => {
this.count++;
this.update();
};
dec = () => {
this.count--;
this.update();
};
template() {
return html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="${this.dec}">-</button>
<span>${this.count}</span>
<button onclick="${this.inc}">+</button>
`;
}
update() {
render(this.shadowRoot, this.template());
}
}
customElements.define("my-counter", MyCounter);
Dependencies
uhtml : ^2.7.3
Component size including library
Minified - Uncompressed | 7,352 |
Minified + Gzip | 3,347 |
Minified + Brotli | 3,001 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
AppRun
AppRun is a JavaScript library for building reliable, high-performance web applications using the Elm inspired architecture, events, and components.
Homepage: https://apprun.js.org/
GitHub: https://github.com/yysun/apprun
Try in WebComponents.dev/** @jsx app.h */
import { app, Component } from "apprun";
const styles = `.my-counter * {
font-size: 200%;
}
.my-counter span {
width: 4rem;
display: inline-block;
text-align: center;
}
.my-counter button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
const add = (state, num) => state + num;
class Counter extends Component {
state = 0;
view = state => <div class="my-counter">
<style>{styles}</style>
<button $onclick={[add, -1]}>-</button>
<span>{state}</span>
<button $onclick={[add, +1]}>+</button>
</div>;
}
app.webComponent('my-counter', Counter);
Dependencies
apprun : ^2.28.3
Component size including library
Minified - Uncompressed | 15,537 |
Minified + Gzip | 5,344 |
Minified + Brotli | 4,754 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
ElemX
Library for connecting MobX to native Web Components with a Vue-like template binding syntax
Homepage: https://github.com/agquick/elemx.js
GitHub: https://github.com/agquick/elemx.js
Try in WebComponents.devimport { observable } from "mobx"; // needs mobx@5.15.7
import { ReactiveElement } from "elemx";
class MyCounterElement extends ReactiveElement {
@observable counter = 0;
templateHTML() {
return `
<button @on-click="this.dec">-</button>
{{this.counter}}
<button @on-click="this.inc">+</button>
`;
}
inc() {
this.counter++;
}
dec() {
this.counter--;
}
templateCSS() {
return `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
}
}
customElements.define("my-counter", MyCounterElement);
Dependencies
elemx : ^0.2.2
mobx : ^6.3.0
Component size including library
Minified - Uncompressed | 102,146 |
Minified + Gzip | 28,905 |
Minified + Brotli | 22,886 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Exalt
A JavaScript framework for building universal apps.
GitHub: https://github.com/exalt/exalt
Try in WebComponents.devimport { Component, html } from "@exalt/core";
const style = /*css*/`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
class MyCounter extends Component {
state = { count: 0 };
render() {
return html`
<button onclick=${() => this.state.count--}>-</button>
<span>${this.state.count}</span>
<button onclick=${() => this.state.count++}>+</button>
`;
}
}
Component.create({ name: "my-counter", styles: [style] }, MyCounter);
Dependencies
@exalt/core : ^0.2.7
Component size including library
Minified - Uncompressed | 5,761 |
Minified + Gzip | 2,459 |
Minified + Brotli | 2,151 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
FAST
The adaptive interface system for modern web experiences
Homepage: https://www.fast.design/
GitHub: https://github.com/microsoft/fast
Try in WebComponents.devimport { html, css, FASTElement } from "@microsoft/fast-element";
class MyCounter extends FASTElement {
static definition = {
name: "my-counter",
template: html<MyCounter>`
<button @click="${(x) => x.count--}">-</button>
<span>${(x) => x.count}</span>
<button @click="${(x) => x.count++}">+</button>
`,
styles: css`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
attributes: ["count"],
};
count: number = 0;
}
FASTElement.define(MyCounter);
Dependencies
@microsoft/fast-element : ^1.0.2
Component size including library
Minified - Uncompressed | 24,040 |
Minified + Gzip | 7,566 |
Minified + Brotli | 6,780 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Compiled and/or Transpiled by WebComponents.dev compiler.Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
HyperHTML Element
Framework agnostic, hyperHTML can be used to render any view, including Custom Elements and Web Components.
GitHub: https://github.com/WebReflection/hyperHTML-Element
Try in WebComponents.devimport HyperHTMLElement from "hyperhtml-element";
class MyCounter extends HyperHTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
created() {
this.count = 0;
this.render();
}
inc = () => {
this.count++;
this.render();
};
dec = () => {
this.count--;
this.render();
};
render() {
return this.html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick=${this.dec}>-</button>
<span>${this.count}</span>
<button onclick=${this.inc}>+</button>
`;
}
}
MyCounter.define("my-counter");
Dependencies
hyperhtml-element : ^3.14.0
Component size including library
Minified - Uncompressed | 22,945 |
Minified + Gzip | 9,170 |
Minified + Brotli | 8,354 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Joist
A small (~2kb) library to help with the creation of web components and web component based applications
Homepage: https://github.com/joist-framework/joist
GitHub: https://github.com/joist-framework/joist
Try in WebComponents.dev// By Danny Blue GitHub:deebloo
import { component, JoistElement, handle, get, State } from "@joist/component";
@component({
tagName: "my-counter",
shadowDom: "open",
styles: [
`* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`,
],
state: 0,
render: ({ state, host, run }) => {
const self = host.shadowRoot;
if (!self.innerHTML) {
self.innerHTML = `
<button id="dec">-</button>
<span id="count"></span>
<button id="inc">+</button>`;
self.getElementById("inc").addEventListener("click", run("add", 1));
self.getElementById("dec").addEventListener("click", run("add", -1));
}
self.getElementById("count").innerHTML = state.toString();
},
})
export class CounterElement extends JoistElement {
@get(State) private state!: State<number>;
@handle("add") add(_: Event, value: number) {
this.state.setValue(this.state.value + value);
}
}
Dependencies
@joist/component : ^1.8.5
Component size including library
Minified - Uncompressed | 6,034 |
Minified + Gzip | 2,224 |
Minified + Brotli | 1,947 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Joist (with lit-html)
A small (~2kb) library to help with the creation of web components and web component based applications
Homepage: https://github.com/joist-framework/joist
GitHub: https://github.com/joist-framework/joist
Try in WebComponents.dev// By Danny Blue GitHub:deebloo
import { component, JoistElement, handle, get, State } from "@joist/component";
import { template } from "@joist/component/lit-html";
import { html } from "lit-html";
@component({
tagName: "my-counter",
shadowDom: "open",
state: 0,
styles: [
`* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`,
],
render: template(({ state, run }) => {
return html`
<button @click="${run("update", -1)}">-</button>
<span>${state}</span>
<button @click="${run("update", 1)}">+</button>
`;
}),
})
export class CounterElement extends JoistElement {
@get(State) private state!: State<number>;
@handle("update")
updateCount(_: Event, num: number) {
return this.state.setValue(this.state.value + num);
}
}
Dependencies
@joist/component : ^1.8.5
lit-html : ^1.4.1
Component size including library
Minified - Uncompressed | 15,224 |
Minified + Gzip | 5,230 |
Minified + Brotli | 4,696 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Lit (new) JavaScript
Lit - Simple. Fast. Web Components.
Homepage: https://lit.dev/
GitHub: https://github.com/lit/lit
Try in WebComponents.devimport { LitElement, html, css } from "lit";
export class MyCounter extends LitElement {
static properties = {
count: { type: Number },
};
static styles = css`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
constructor() {
super();
this.count = 0;
}
inc() {
this.count++;
}
dec() {
this.count--;
}
render() {
return html`
<button @click="${this.dec}">-</button>
<span>${this.count}</span>
<button @click="${this.inc}">+</button>
`;
}
}
customElements.define("my-counter", MyCounter);
Dependencies
lit : ^2.0.0-rc.1
Component size including library
Minified - Uncompressed | 15,528 |
Minified + Gzip | 5,819 |
Minified + Brotli | 5,223 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Lit (new) TypeScript
Lit - Simple. Fast. Web Components.
Homepage: https://lit.dev/
GitHub: https://github.com/lit/lit
Try in WebComponents.devimport {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('my-counter')
export class SimpleGreeting extends LitElement {
@property() public count = 0;
static styles = css`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
render() {
return html`
<button @click="${this.dec}">-</button>
<span>${this.count}</span>
<button @click="${this.inc}">+</button>
`;
}
inc() {
this.count++;
}
dec() {
this.count--;
}
}
Dependencies
lit : ^2.0.0-rc.1
Component size including library
Minified - Uncompressed | 16,643 |
Minified + Gzip | 6,187 |
Minified + Brotli | 5,576 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
LitElement v2
Homepage: https://lit-element.polymer-project.org/
GitHub: https://github.com/polymer/lit-element
Try in WebComponents.devimport { LitElement, html, css } from 'lit-element';
export class MyCounter extends LitElement {
static properties = {
count: { type: Number },
};
static styles = css`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
constructor() {
super();
this.count = 0;
}
inc() {
this.count++;
}
dec() {
this.count--;
}
render() {
return html`
<button @click="${this.dec}">-</button>
<span>${this.count}</span>
<button @click="${this.inc}">+</button>
`;
}
}
customElements.define('my-counter', MyCounter);
Dependencies
lit-element : ^2.5.1
Component size including library
Minified - Uncompressed | 21,060 |
Minified + Gzip | 6,877 |
Minified + Brotli | 6,196 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Neow (Alpha)
Modern, fast, and light it's the front-end framework to fall inlove with. (ALPHA)
Homepage: https://neow.dev
GitHub: (alpha)
Try in WebComponents.devimport { ComponentMixin } from "@neow/core";
class MyComponent extends ComponentMixin(HTMLElement) {
static template = `
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="{{this.dec()}}">-</button>
<span>{{this.counter}}</span>
<button onclick="{{this.inc()}}">+</button>
`;
counter = 0;
inc() {
this.counter++;
}
dec() {
this.counter--;
}
}
customElements.define("my-counter", MyComponent);
Dependencies
@neow/core : ^1.0.1
Component size including library
Minified - Uncompressed | 4,415 |
Minified + Gzip | 1,993 |
Minified + Brotli | 1,719 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Omi w/Class
Front End Cross-Frameworks Framework - Merge Web Components, JSX, Virtual DOM, Functional style, observe or Proxy into one framework with tiny size and high performance. Write components once, using in everywhere, such as Omi, React, Preact, Vue or Angular.
Homepage: http://omijs.org
GitHub: https://github.com/Tencent/omi
Try in WebComponents.devimport { define, WeElement, html } from "omi";
class MyCounter extends WeElement {
static get propTypes() {
return {
count: Number,
};
}
static get defaultProps() {
return { count: 0 };
}
install() {
this.data = { count: this.props.count };
}
static get css() {
return `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
}
inc = () => {
this.data.count++;
this.update();
};
dec = () => {
this.data.count--;
this.update();
};
render(props) {
return html`
<button onclick="${this.dec}">-</button>
<span>${this.data.count}</span>
<button onclick="${this.inc}">+</button>
`;
}
}
define("my-counter", MyCounter);
Dependencies
omi : ^6.19.3
Component size including library
Minified - Uncompressed | 24,622 |
Minified + Gzip | 8,412 |
Minified + Brotli | 7,566 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Readymade
JavaScript microlibrary for developing Web Components with Decorators
Homepage: https://github.com/readymade-ui/readymade
GitHub: https://github.com/readymade-ui/readymade
Try in WebComponents.devimport { Component, State, CustomElement, html, css } from '@readymade/core';
@Component({
selector: 'my-counter',
template: html`
<button id="dec">-</button>
<span>{{ c }}</span>
<button id="inc">+</button>
`,
style: css`
span,
button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`
})
export class MyCounter extends CustomElement {
private $state: {
c: 0;
};
connectedCallback() {
this.shadowRoot
.querySelector('#inc')
.addEventListener('click', this.inc.bind(this));
this.shadowRoot
.querySelector('#dec')
.addEventListener('click', this.dec.bind(this));
}
get count() {
return this.$state.c;
}
inc() {
this.setState('c', this.count + 1);
}
dec() {
this.setState('c', this.count - 1);
}
@State()
public getState() {
return { c: 0 };
}
}
Dependencies
@readymade/core : ^2.0.1
Component size including library
Minified - Uncompressed | 7,133 |
Minified + Gzip | 2,925 |
Minified + Brotli | 2,574 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
SkateJS (with Lit-html)
SkateJS - Effortless custom elements powered by modern view libraries.
Homepage: https://skatejs.netlify.com/
GitHub: https://github.com/skatejs/skatejs
Try in WebComponents.devimport Element from "@skatejs/element";
import { render, html } from "lit-html";
class MyCounterElement extends Element {
static get props() {
return {
count: Number,
};
}
inc = () => {
this.count++;
};
dec = () => {
this.count--;
};
render() {
const style = `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
return html`
<style>
${style}
</style>
<button @click="${this.dec}">
-
</button>
<span>${this.count}</span>
<button @click="${this.inc}">
+
</button>
`;
}
renderer() {
return render(this.render(), this.renderRoot);
}
}
customElements.define("my-counter", MyCounterElement);
Dependencies
@skatejs/element : 0.0.1
lit-html : ^1.4.1
Component size including library
Minified - Uncompressed | 15,539 |
Minified + Gzip | 5,247 |
Minified + Brotli | 4,703 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
SkateJS (with Preact)
SkateJS - Effortless custom elements powered by modern view libraries.
Homepage: https://skatejs.netlify.com/
GitHub: https://github.com/skatejs/skatejs
Try in WebComponents.dev/** @jsx h **/
import Element, { h } from "@skatejs/element-preact";
class MyCounterElement extends Element {
static get props() {
return {
count: Number,
};
}
inc = () => {
this.count++;
};
dec = () => {
this.count--;
};
render() {
const style = `host * {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
return (
<host>
<style>{style}</style>
<button onclick={this.dec}>-</button>
<span>{this.count}</span>
<button onclick={this.inc}>+</button>
</host>
);
}
}
customElements.define("my-counter", MyCounterElement);
Dependencies
@skatejs/element-preact : 0.0.1
Component size including library
Minified - Uncompressed | 17,508 |
Minified + Gzip | 6,251 |
Minified + Brotli | 5,685 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
SlimJS
Slim.js is a lightning fast library for development of native Web Components and Custom Elements based on modern standards. No black magic involved, no useless dependencies.
Homepage: https://slimjs.com
GitHub: https://github.com/slimjs/slim.js
Try in WebComponents.devimport { Slim } from "slim-js/Slim.js";
import { tag, template, useShadow } from "slim-js/Decorators";
@tag("my-counter")
@useShadow(true)
@template(`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style >
<button click="dec">-</button>
<span>{{parseCount(count)}}</span>
<button click="inc">+</button>
`)
class MyCounter extends Slim {
constructor() {
super();
this.count = 0;
}
parseCount(num) {
return String(num);
}
inc() {
this.count++;
}
dec() {
this.count--;
}
}
Dependencies
slim-js : ^4.0.7
Component size including library
Minified - Uncompressed | 9,519 |
Minified + Gzip | 3,675 |
Minified + Brotli | 3,236 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
uce-template
A tiny toolless library with tools included
Homepage: https://medium.com/@WebReflection/a-new-web-components-wonder-f9e042785a91
GitHub: https://github.com/WebReflection/uce-template
Try in WebComponents.dev<my-counter>
<button onclick="{{dec}}">
-
</button>
<span>{{state.count}}</span>
<button onclick="{{inc}}">
+
</button>
</my-counter>
<script type="module">
import reactive from "@uce/reactive";
export default {
setup(element) {
const state = reactive({ count: 0 });
const inc = () => {
state.count++;
};
const dec = () => {
state.count--;
};
return { state, inc, dec };
},
};
</script>
<style scoped>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
Dependencies
uce-template : ^0.6.0
Component size including library
Minified - Uncompressed | 25,128 |
Minified + Gzip | 10,055 |
Minified + Brotli | 9,104 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Atomico
Atomico is a microlibrary (3.9kB) inspired by React Hooks, designed and optimized for the creation of small, powerful, declarative webcomponents and only using functions
Homepage: https://atomico.gitbook.io/doc/
GitHub: https://github.com/atomicojs/atomico
Try in WebComponents.devimport { c, useProp } from "atomico";
const style = /*css*/ `
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`;
function myCounter() {
let [count, setCount] = useProp("count");
return (
<host shadowDom>
<style>{style}</style>
<button onclick={() => setCount(count - 1)}>-</button>
<span>{count}</span>
<button onclick={() => setCount(count + 1)}>+</button>
</host>
);
}
myCounter.props = {
count: {
type: Number,
reflect: true,
value: 0,
},
};
customElements.define("my-counter", c(myCounter));
Dependencies
atomico : ^1.20.1
Component size including library
Minified - Uncompressed | 6,043 |
Minified + Gzip | 2,919 |
Minified + Brotli | 2,604 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
{
"presets": [
[
"@babel/preset-react",
{
"throwIfNamespace": false,
"runtime": "automatic",
"importSource": "atomico"
}
]
]
}
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Gallop
Use full power of web components
GitHub: https://github.com/tarnishablec/gallop
Try in WebComponents.devimport { component, css, useState, useStyle, html } from "@gallop/gallop";
component("my-counter", () => {
const [state] = useState({ count: 0 });
useStyle(
() => css`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
[]
);
return html`
<button @click="${() => state.count--}">
-
</button>
<span>${state.count}</span>
<button @click="${() => state.count++}">
+
</button>
`;
});
Dependencies
@gallop/gallop : ^0.12.0
Component size including library
Minified - Uncompressed | 11,241 |
Minified + Gzip | 4,330 |
Minified + Brotli | 3,882 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Haunted
React's Hooks API implemented for web components 👻
GitHub: https://github.com/matthewp/haunted
Try in WebComponents.devimport { html } from "lit-html";
import { component, useState } from "haunted";
function Counter() {
const [count, setCount] = useState(0);
return html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button @click=${() => setCount(count - 1)}>
-
</button>
<span>${count}</span>
<button @click=${() => setCount(count + 1)}>
+
</button>
`;
}
customElements.define("my-counter", component(Counter));
Dependencies
haunted : ^4.8.0
Component size including library
Minified - Uncompressed | 15,623 |
Minified + Gzip | 5,384 |
Minified + Brotli | 4,809 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Heresy w/Hook
Don't simulate the DOM. Be the DOM. React-like Custom Elements via the V1 API built-in extends.
Homepage: https://medium.com/@WebReflection/any-holy-grail-for-web-components-c3d4973f3f3f
GitHub: https://github.com/WebReflection/heresy
Try in WebComponents.devimport { define } from "heresy";
define("MyCounter", {
style: (MyCounter) => `
${MyCounter} * {
font-size: 200%;
}
${MyCounter} span {
width: 4rem;
display: inline-block;
text-align: center;
}
${MyCounter} button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
render({ useState }) {
const [count, update] = useState(0);
this.html`
<button onclick="${() => update(count - 1)}">-</button>
<span>${count}</span>
<button onclick="${() => update(count + 1)}">+</button>
`;
},
});
Dependencies
heresy : ^1.0.4
Component size including library
Minified - Uncompressed | 21,525 |
Minified + Gzip | 8,797 |
Minified + Brotli | 7,985 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Ficus w/ lit-html
Lightweight functions for developing applications using web components
Homepage: https://docs.ficusjs.org/
GitHub: https://github.com/ficusjs/ficusjs
Try in WebComponents.devimport { render as renderer, html } from "lit-html";
import { createComponent } from "ficusjs";
createComponent("my-counter", {
root: "shadow",
state() {
return { count: 0 };
},
renderer,
inc() {
this.state.count++;
},
dec() {
this.state.count--;
},
render() {
return html`
<style>
span,
button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<div>
<button @click="${this.dec}">-</button>
<span>${this.state.count}</span>
<button @click="${this.inc}">+</button>
</div>
`;
},
});
Dependencies
ficusjs : ^3.3.0
lit-html : ^1.4.1
Component size including library
Minified - Uncompressed | 15,628 |
Minified + Gzip | 5,309 |
Minified + Brotli | 4,738 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Heresy
Don't simulate the DOM. Be the DOM. React-like Custom Elements via the V1 API built-in extends.
Homepage: https://medium.com/@WebReflection/any-holy-grail-for-web-components-c3d4973f3f3f
GitHub: https://github.com/WebReflection/heresy
Try in WebComponents.devimport { define } from "heresy";
define("MyCounter", {
style: (MyCounter) => `
${MyCounter} * {
font-size: 200%;
}
${MyCounter} span {
width: 4rem;
display: inline-block;
text-align: center;
}
${MyCounter} button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
oninit() {
this.count = 0;
},
onclick({ currentTarget }) {
this[currentTarget.dataset.op]();
this.render();
},
inc() {
this.count++;
},
dec() {
this.count--;
},
render() {
this.html`
<button data-op="dec" onclick="${this}">-</button>
<span>${this.count}</span>
<button data-op="inc" onclick="${this}">+</button>
`;
},
});
Dependencies
heresy : ^1.0.4
Component size including library
Minified - Uncompressed | 21,648 |
Minified + Gzip | 8,837 |
Minified + Brotli | 8,020 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
htmlhammer
Write HTML with JavaScript using real HTML tag names.
GitHub: https://github.com/vsmid/htmlhammer
Try in WebComponents.devimport { style, button, span, customElement } from 'htmlhammer';
const MyCounterStyle = style(/*css*/`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`);
const MyCounter = customElement("my-counter", {
Count: 0,
CounterDisplay: null,
connectedCallback() {
this.attachShadow({ mode: "open" });
this.shadowRoot.append(
MyCounterStyle,
button({ id: "dec", onclick: this.Dec }, "-"),
this.CounterDisplay = span(this.Count),
button({ id: "inc", onclick: this.Inc }, "+")
);
},
Inc() {
this.Update(++this.Count);
},
Dec() {
this.Update(--this.Count);
},
Update(count) {
this.CounterDisplay.textContent = count;
}
});
Dependencies
htmlhammer : ^3.5.1
Component size including library
Minified - Uncompressed | 5,959 |
Minified + Gzip | 2,763 |
Minified + Brotli | 2,298 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Hybrids
Hybrids is a UI library for creating web components with strong declarative and functional approach based on plain objects and pure functions.
Homepage: https://hybrids.js.org
GitHub: https://github.com/hybridsjs/hybrids
Try in WebComponents.devimport { html, define } from "hybrids";
function inc(host) {
host.count++;
}
function dec(host) {
host.count--;
}
const MyCounter = {
count: 0,
render: ({ count }) => html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="${dec}">-</button>
<span>${count}</span>
<button onclick="${inc}">+</button>
`,
};
define("my-counter", MyCounter);
Dependencies
hybrids : ^5.2.2
Component size including library
Minified - Uncompressed | 15,760 |
Minified + Gzip | 5,970 |
Minified + Brotli | 5,366 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Kirei
An in browser implementation of Vue 3 (limited to Composition API)
GitHub: https://github.com/ifaxity/kirei
Try in WebComponents.devimport { defineComponent, html, css, ref } from '@kirei/element';
defineComponent({
name: 'MyCounter',
props: {},
styles: css`* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
setup(props, ctx) {
const count = ref(0);
function dec() {
count.value--;
}
function inc() {
count.value++;
}
return () => html`
<button @click=${dec}>-</button>
<span>${count}</span>
<button @click=${inc}>+</button>
`;
}
});
Dependencies
@kirei/element : ^2.0.4
Component size including library
Minified - Uncompressed | 28,933 |
Minified + Gzip | 11,297 |
Minified + Brotli | 10,246 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Litedom
A very small (3kb) view library that is packed: Web Components, Custom Element, Template Literals, Reactive, Data Binding, One Way Data Flow, Two-way data binding, Event Handling, Props, Lifecycle, State Management, Computed Properties, Directives. No dependencies, no virtual dom, no build tool. Wow! ...but it's a just a view library!
Homepage: https://litedom.js.org/
GitHub: https://github.com/mardix/litedom
Try in WebComponents.devimport Litedom from "litedom";
Litedom({
tagName: "my-counter",
shadowDOM: true,
template: `
<button @click="dec">-</button>
<span>{this.count}</span>
<button @click="inc">+</button>
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
`,
data: {
count: 0,
},
dec() {
this.data.count--;
},
inc() {
this.data.count++;
},
});
Dependencies
litedom : ^0.12.1
Component size including library
Minified - Uncompressed | 9,476 |
Minified + Gzip | 3,855 |
Minified + Brotli | 3,421 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Ottavino
Tiny, Fast and Declarative User Interface Development. Using native custom elements API (but not only). As simple as it gets.
GitHub: https://github.com/betterthancode/ottavino
Try in WebComponents.devimport { component } from "ottavino";
component({
tag: "my-counter",
shadow: true,
template: `
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="{{this.decrease()}}">-</button>
<span>{{this.count}}</span>
<button onclick="{{this.increase()}}" >+</button>
`,
properties: {
count: 0,
},
this: {
increase: function () {
this.count++;
},
decrease: function () {
this.count--;
},
},
});
Dependencies
ottavino : ^0.2.4
Component size including library
Minified - Uncompressed | 4,474 |
Minified + Gzip | 1,984 |
Minified + Brotli | 1,719 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Swiss
Functional custom elements
GitHub: https://github.com/luwes/swiss
Try in WebComponents.devimport { define } from "swiss";
import { html, render } from "lit-html";
const Counter = (CE) => (el) => {
el.attachShadow({ mode: "open" });
return {
update: () =>
render(
html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button @click="${() => el.count--}">-</button>
<span>${el.count}</span>
<button @click="${() => el.count++}">+</button>
`,
el.shadowRoot
),
};
};
define("my-counter", {
props: { count: 0 },
setup: Counter,
});
Dependencies
swiss : ^2.1.0
Component size including library
Minified - Uncompressed | 12,566 |
Minified + Gzip | 4,550 |
Minified + Brotli | 4,065 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Synergy
Synergy.js is a 4kB runtime library that gives you everything you need to define Web Components using standard HTML, JavaScript and CSS.
Homepage: https://synergyjs.org/
GitHub: https://github.com/defx/synergy
Try in WebComponents.devimport { define } from 'synergy';
const style = /*css*/`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
const template = /*css*/`
<style>${style}</style>
<button onclick="dec()">
-
</button>
<span>{{ count }}</span>
<button onclick="inc()">
+
</button>`;
const model = ({ count = 0 }) => {
return { count,
inc() {
this.count++;
},
dec() {
this.count--;
}
};
};
define("my-counter", model, template, { shadow: 'open' });
Dependencies
synergy : ^5.0.0
Component size including library
Minified - Uncompressed | 10,001 |
Minified + Gzip | 4,148 |
Minified + Brotli | 3,711 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
uce
µhtml based Custom Elements.
Homepage: https://github.com/WebReflection/uce
GitHub: https://github.com/WebReflection/uce
Try in WebComponents.devimport "uce";
const MyCounter = ({ define }) => {
define("my-counter", {
attachShadow: { mode: "open" },
props: { count: 0 },
bound: ["inc", "dec"],
inc() {
this.count++;
this.render();
},
dec() {
this.count--;
this.render();
},
render() {
this.html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick="${this.dec}">-</button>
<span>${this.count}</span>
<button onclick="${this.inc}">+</button>
`;
},
});
};
customElements
.whenDefined("uce-lib")
.then(() => MyCounter(customElements.get("uce-lib")));
Dependencies
uce : ^1.16.2
Component size including library
Minified - Uncompressed | 10,408 |
Minified + Gzip | 4,620 |
Minified + Brotli | 4,171 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
twind
Homepage: https://twind.dev/
GitHub: https://github.com/tw-in-js/twind
Try in WebComponents.devimport { create, cssomSheet } from 'twind'
// 1. Create separate CSSStyleSheet
const sheet = cssomSheet({ target: new CSSStyleSheet() })
// 2. Use that to create an own twind instance
const { tw } = create({ sheet })
const template = document.createElement("template");
template.innerHTML = /*html*/`
<p class="${tw`invisible`}">This uses CSSStyleSheet but<br>it's not supported in Firefox.</p>
<button class="${tw`rounded-lg px-7 py-4 text(white 3xl) bg-green-600`}" id="dec">-</button>
<span class="${tw`inline-block w-14 text(3xl center)`}" id="count"></span>
<button class="${tw`rounded-lg px-7 py-4 text(white 3xl) bg-green-600`}" id="inc">+</button>`;
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: "open" });
this.shadowRoot.adoptedStyleSheets = [sheet.target];
}
connectedCallback() {
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.shadowRoot.getElementById("inc").onclick = () => this.inc();
this.shadowRoot.getElementById("dec").onclick = () => this.dec();
this.update(this.count);
}
inc() {
this.update(++this.count);
}
dec() {
this.update(--this.count);
}
update(count) {
this.shadowRoot.getElementById("count").innerHTML = count;
}
}
customElements.define("my-counter", MyCounter);
Dependencies
twind : ^0.16.13
Component size including library
Minified - Uncompressed | 35,333 |
Minified + Gzip | 14,185 |
Minified + Brotli | 12,737 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
twind + LitElement
twind - A small compiler that turns Tailwind short hand into CSS rules at run, build or serve time. If you have used Tailwind and/or a CSS-in-JS solution before then most of the API will feel very familiar.
Homepage: https://twind.dev/
GitHub: https://github.com/tw-in-js/twind
Try in WebComponents.devimport { LitElement, html } from "lit-element";
import { create, cssomSheet } from 'twind'
// 1. Create separate CSSStyleSheet
const sheet = cssomSheet({ target: new CSSStyleSheet() })
// 2. Use that to create an own twind instance
const { tw } = create({ sheet })
export class MyCounter extends LitElement {
static properties = {
count: { type: Number },
};
static styles = [sheet.target];
constructor() {
super();
this.count = 0;
}
inc() {
this.count++;
}
dec() {
this.count--;
}
render() {
return html`
<p class="${tw`invisible`}">This uses CSSStyleSheet but<br>it's not supported in Firefox.</p>
<button class="${tw`rounded-lg px-7 py-4 text(white 3xl) bg-green-600`}" @click="${this.dec}">-</button>
<span class="${tw`inline-block w-14 text(3xl center)`}">${this.count}</span>
<button class="${tw`rounded-lg px-7 py-4 text(white 3xl) bg-green-600`}" @click="${this.inc}">+</button>
`;
}
}
customElements.define("my-counter", MyCounter);
Dependencies
twind : ^0.16.13
lit-element : ^2.5.1
Component size including library
Minified - Uncompressed | 55,701 |
Minified + Gzip | 20,321 |
Minified + Brotli | 18,323 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
{
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"decoratorsBeforeExport": true
}
],
"@babel/plugin-proposal-class-properties"
]
}
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Lightning Web Components
⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
Homepage: https://lwc.dev
GitHub: https://github.com/salesforce/lwc
Try in WebComponents.devimport { LightningElement, api, buildCustomElementConstructor } from "lwc";
export default class MyCounter extends LightningElement {
count = 0;
inc() {
this.count++;
}
dec() {
this.count--;
}
}
customElements.define("my-counter", buildCustomElementConstructor(MyCounter));
Dependencies
lwc : ^1.18.0
Component size including library
Minified - Uncompressed | 45,650 |
Minified + Gzip | 14,879 |
Minified + Brotli | 13,324 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following LWC settings:
const outputConfig: {
format: 'esm',
sourcemap: true,
};
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Lume Element
Create Custom Elements with reactivity and automatic re-rendering
GitHub: https://github.com/lume/element
Try in WebComponents.devimport {Element, reactify} from '@lume/element'
import {html} from '@lume/element/dist/html'
export class MyCounter extends Element {
count = 0;
constructor() {
super()
reactify(this, ['count'])
}
template = () => html`
<button onclick=${() => (this.count -= 1)}>-</button>
<span>${() => this.count}</span>
<button onclick=${() => (this.count += 1)}>+</button>
`
css = /*css*/ `
* { font-size: 200%; }
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem; height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
outline: none;
cursor: pointer;
}
`
}
customElements.define('my-counter', MyCounter)
Dependencies
@lume/element : ^0.5.5
Component size including library
Minified - Uncompressed | 57,094 |
Minified + Gzip | 19,867 |
Minified + Brotli | 18,055 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with thesolid
preset:
{
"presets": [
"solid"
]
}
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Prism
Prism is a experimental compiler for building isomorphic web applications with web components.
GitHub: https://github.com/kaleidawave/prism
Try in WebComponents.dev<template>
<button @click="dec">
-
</button>
<span>{count}</span>
<button @click="inc">
+
</button>
</template>
<script>
@Default({count: 0})
class MyCounter extends Component<{count: number}> {
inc() {
this.data.count++;
}
dec() {
this.data.count--;
}
}
</script>
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
Dependencies
@kaleidawave/prism :
Component size including library
Minified - Uncompressed | 1,887 |
Minified + Gzip | 965 |
Minified + Brotli | 795 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Solid Element
The Deceptively Simple User Interface Library
GitHub: https://github.com/ryansolid/solid
Try in WebComponents.devimport { createSignal } from "solid-js";
import { customElement } from "solid-element";
const style = `div * {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
customElement("my-counter", () => {
const [count, setCount] = createSignal(0);
return (
<div>
<style>{style}</style>
<button onClick={() => setCount(count() - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(count() + 1)}>+</button>
</div>
);
});
Dependencies
solid-js : ^0.26.5
solid-element : ^0.26.5
Component size including library
Minified - Uncompressed | 10,877 |
Minified + Gzip | 4,206 |
Minified + Brotli | 3,752 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with thesolid
preset:
{
"presets": [
"solid"
]
}
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Stencil
Stencil is a toolchain for building reusable, scalable Design Systems. Generate small, blazing fast, and 100% standards based Web Components that run in every browser.
Homepage: https://stenciljs.com/
GitHub: https://github.com/ionic-team/stencil
Try in WebComponents.dev/* @jsx h */
import { h, Component, State, Host } from "@stencil/core";
@Component({
tag: "my-counter",
styleUrl: "index.css",
shadow: true,
})
export class MyCounter {
@State() count: number = 0;
inc() {
this.count++;
}
dec() {
this.count--;
}
render() {
return (
<Host>
<button onClick={this.dec.bind(this)}>-</button>
<span>{this.count}</span>
<button onClick={this.inc.bind(this)}>+</button>
</Host>
);
}
}
Dependencies
@stencil/core : ^2.5.2
Component size including library
Minified - Uncompressed | 13,056 |
Minified + Gzip | 5,456 |
Minified + Brotli | 4,921 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been compiled with Stencil CLI and settings:
// stencil.tsconfig.js
...
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"declaration": false,
"experimentalDecorators": true,
"lib": ["DOM", "ES2018"],
"moduleResolution": "node",
"module": "esnext",
"target": "es2017",
"noUnusedLocals": true,
"noUnusedParameters": true,
"jsx": "react",
"jsxFactory": "h",
"types": []
}
...
//stencil.config.js
import { Config } from '@stencil/core';
export const config: Config = {
srcDir: 'stencil/src',
namespace: 'my-counter',
tsconfig: 'stencil.tsconfig.json',
hashFileNames: false,
plugins: [],
outputTargets: [
{
type: 'dist-custom-elements-bundle',
},
],
hydratedFlag: null,
extras: {
cssVarsShim: false,
dynamicImportShim: false,
safari10: false,
scriptDataOpts: false,
shadowDomShim: false,
},
};
Bundling configuration
See details
Bundled by Stencil CLISvelte (with {customElement: true})
Cybernetically enhanced web apps
Homepage: https://svelte.dev/
GitHub: https://github.com/sveltejs/svelte
Try in WebComponents.dev<svelte:options tag="my-counter" />
<script>
let count = 0;
function inc() {
count++;
}
function dec() {
count--;
}
</script>
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button on:click={dec}>
-
</button>
<span>{count}</span>
<button on:click={inc}>
+
</button>
Dependencies
svelte : ^3.38.2
Component size including library
Minified - Uncompressed | 4,020 |
Minified + Gzip | 1,885 |
Minified + Brotli | 1,661 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Svelte settings:
const options = {
format: 'esm',
customElement: true
};
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Angular 11 Elements (wrapped with angular-elements)
One framework. Mobile & desktop.
Homepage: https://angular.io/
GitHub: https://github.com/angular/angular
Try in WebComponents.devimport {
Input,
Component,
ViewEncapsulation,
ChangeDetectorRef,
} from "@angular/core";
@Component({
selector: "my-counter",
template: `
<button (click)="dec()">-</button>
<span>{{count}}</span>
<button (click)="inc()">+</button>
`,
styles: [
`
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
`,
],
encapsulation: ViewEncapsulation.ShadowDom,
})
export default class MyCounter {
@Input() count: number = 0;
constructor(private cd: ChangeDetectorRef) {}
dec() {
this.count--;
this.cd.detectChanges();
}
inc() {
this.count++;
this.cd.detectChanges();
}
}
Dependencies
@angular/core : ^11.2.13
@angular/cli : ^11.2.12
@angular/common : ^11.2.13
@angular/compiler : ^11.2.13
@angular/compiler-cli : ^11.2.13
@angular-devkit/build-angular : ^0.1102.12
document-register-element : ^1.14.10
@angular/elements : ^11.2.13
@angular/platform-browser-dynamic : ^11.2.13
@angular/platform-browser : ^11.2.13
Component size including library
Minified - Uncompressed | 180,509 |
Minified + Gzip | 50,771 |
Minified + Brotli | 44,706 |
Composition
Open Bundle VisualizerCompilation configuration
See details
Compile with Angular compiler and the followingangular.json
:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "download",
"projects": {
"elements": {
"root": "download/011-angular-elements",
"sourceRoot": "download/011-angular-elements/src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"aot": true,
"outputPath": "download/011-angular-elements/dist",
"index": "download/011-angular-elements/src/index.html",
"main": "download/011-angular-elements/src/index.ts",
"tsConfig": "angular.tsconfig.json",
"polyfills": "polyfills.ts",
"assets": [],
"styles": [],
"scripts": [
{
"input": "node_modules/document-register-element/build/document-register-element.js"
}
]
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "none",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": false,
"vendorChunk": true,
"buildOptimizer": true
}
}
}
}
}
},
"defaultProject": "elements",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"style": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}
And following tsconfig.json
:
{
"compileOnSave": false,
"compilerOptions": {
"module": "esnext",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2016",
"dom"
],
"paths": {
"EnumUtils": [
"dist/enum-utils"
],
"EnumUtils/*": [
"dist/enum-utils/*"
]
}
},
"files": [
"download/011-angular-elements/src/index.ts",
"polyfills.ts"
]
}
Bundling configuration
See details
Bundled with Angular CLIPreact w/Class (wrapped with preact-custom-element)
Fast 3kB alternative to React with the same modern API.
Homepage: https://preactjs.com/
GitHub: https://github.com/preactjs/preact
Try in WebComponents.devimport { createCustomElement } from "@wcd/preact-custom-element";
import { Component, html } from "htm/preact";
import "preact";
class MyCounter extends Component {
state = {
count: 0,
};
inc = () => {
this.setState((prev) => ({ count: prev.count + 1 }));
};
dec = () => {
this.setState((prev) => ({ count: prev.count - 1 }));
};
render(props, state) {
return html`
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onClick=${this.dec}>
-
</button>
<span>${state.count}</span>
<button onClick=${this.inc}>
+
</button>
`;
}
}
customElements.define("my-counter", createCustomElement(MyCounter, ["count"]));
Dependencies
preact : ^10.5.13
@wcd/preact-custom-element : ^3.1.3
Component size including library
Minified - Uncompressed | 12,101 |
Minified + Gzip | 4,926 |
Minified + Brotli | 4,500 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:
[
[
Babel.availablePresets['env'],
{
targets: {
browsers:
'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
},
modules: 'false',
bugfixes: true,
},
],
[Babel.availablePresets['react']],
],
plugins: [
[Babel.availablePlugins['proposal-decorators'], { legacy: true }],
[Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
React w/Class (wrapped with react-to-webcomponent)
A JavaScript library for building user interfaces
Homepage: https://reactjs.org/
GitHub: https://github.com/facebook/react/
Try in WebComponents.devimport React from "react";
import ReactDOM from "react-dom";
import reactToWebComponent from "react-to-webcomponent";
interface State {
count: number;
}
interface Props {}
export default class MyCounter extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
const styles = `.my-counter * {
font-size: 200%;
}
.my-counter span {
width: 4rem;
display: inline-block;
text-align: center;
}
.my-counter button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
return (
<div className="my-counter">
<style>{styles}</style>
<button onClick={() => this.setState({ count: this.state.count - 1 })}>
-
</button>
<span>{this.state.count}</span>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
+
</button>
</div>
);
}
}
customElements.define(
"my-counter",
reactToWebComponent(MyCounter, React, ReactDOM)
);
Dependencies
react : ^17.0.2
react-dom : ^17.0.2
react-to-webcomponent : ^1.5.1
Component size including library
Minified - Uncompressed | 131,810 |
Minified + Gzip | 42,501 |
Minified + Brotli | 37,350 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following TypeScript settings: {
module: ts.ModuleKind.ESNext,
experimentalDecorators: true,
emitDecoratorMetadata: true,
importHelpers: true,
lib: ['ESNext', 'dom'],
target: ts.ScriptTarget.ESNext
};
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
React w/Hook (wrapped with react-to-webcomponent)
A JavaScript library for building user interfaces
Homepage: https://reactjs.org/
GitHub: https://github.com/facebook/react/
Try in WebComponents.devimport React, { useState } from "react";
import ReactDOM from "react-dom";
import reactToWebComponent from "react-to-webcomponent";
export default function MyCounter() {
const [count, setCount] = useState(0);
const styles = `
.my-counter * {
font-size: 200%;
}
.my-counter span {
width: 4rem;
display: inline-block;
text-align: center;
}
.my-counter button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}`;
return (
<div className="my-counter">
<style>{styles}</style>
<button onClick={() => setCount(count - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
customElements.define(
"my-counter",
reactToWebComponent(MyCounter, React, ReactDOM)
);
Dependencies
react : ^17.0.2
react-dom : ^17.0.2
react-to-webcomponent : ^1.5.1
Component size including library
Minified - Uncompressed | 131,686 |
Minified + Gzip | 42,475 |
Minified + Brotli | 37,369 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following TypeScript settings: {
module: ts.ModuleKind.ESNext,
experimentalDecorators: true,
emitDecoratorMetadata: true,
importHelpers: true,
lib: ['ESNext', 'dom'],
target: ts.ScriptTarget.ESNext
};
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Riot (wrapped with @riotjs/custom-elements)
Simple and elegant component-based UI library
Homepage: https://riot.js.org/
GitHub: https://github.com/riot/riot
Try in WebComponents.dev<my-component>
<style>
* {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
<button onclick={dec}>
-
</button>
<span>{state.count}</span>
<button onclick={inc}>
+
</button>
<script>
export default {
onBeforeMount(props, state) {
this.state = {
count: 0
}
},
inc() {
this.update({
count: this.state.count+1
})
},
dec() {
this.update({
count: this.state.count-1
})
},
}
</script>
</my-component>
Dependencies
riot : ^5.4.1
@riotjs/custom-elements : ^5.0.0
Component size including library
Minified - Uncompressed | 17,193 |
Minified + Gzip | 6,203 |
Minified + Brotli | 5,571 |
Composition
Open Bundle VisualizerCompilation configuration
See details
All Sources have been Transpiled by WebComponents.dev compiler with the following Riot settings:
const settings = {
scopedCss: false,
};
Bundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Vue2 (wrapped with vue-custom-element)
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
Homepage: https://vuejs.org/
GitHub: https://github.com/vuejs/vue
Try in WebComponents.dev<template>
<div>
<button @click="this.dec">
-
</button>
<span>{{count}}</span>
<button @click="this.inc">
+
</button>
</div>
</template>
<script>
export default {
tag: 'my-counter',
name: 'MyCounter',
data() {
return { count: 0 }
},
methods: {
inc: function() {
this.count++;
},
dec: function() {
this.count--;
}
}
};
</script>
<style scoped>
span,
button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 64px;
height: 64px;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
Dependencies
vue : ^2.6.12
vue-custom-element : ^3.3.0
Component size including library
Minified - Uncompressed | 76,976 |
Minified + Gzip | 27,180 |
Minified + Brotli | 24,338 |
Composition
Open Bundle VisualizerCompilation configuration
See details
Vue sources have been Transpiled by rollup-plugin-vueBundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Vue3 `One Piece` (wrapped within a minimum Web Component layer)
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
Homepage: https://vuejs.org/
GitHub: https://github.com/vuejs/vue
Try in WebComponents.dev<template>
<div>
<button @click="dec">
-
</button>
<span>{{ state.count }}</span>
<button @click="inc">
+
</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({
count: 0,
});
function inc() {
state.count++;
}
function dec() {
state.count--;
}
return {
state,
inc,
dec,
};
},
};
</script>
<style scoped>
span,
button {
font-size: 200%;
}
span {
width: 4rem;
display: inline-block;
text-align: center;
}
button {
width: 4rem;
height: 4rem;
border: none;
border-radius: 10px;
background-color: seagreen;
color: white;
}
</style>
Dependencies
vue@next :
Component size including library
Minified - Uncompressed | 45,814 |
Minified + Gzip | 18,103 |
Minified + Brotli | 16,441 |
Composition
Open Bundle VisualizerCompilation configuration
See details
Vue sources have been Transpiled by rollup-plugin-vueBundling configuration
See details
Bundled with Rollup
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
...
input: 'source.js',
output: {
dir: 'dist'),
format: 'esm',
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
withdeps ? resolve() : undefined,
commonjs( /* { namedExports: for React } */ ),
terser({ output: { comments: false } }),
...
Bundle Size Comparison
Bundle size of 1 component
If you only deliver a single <my-counter/>
you have the full cost of the library for a single component.
This is the total cost of a single component with the library included.
Notes
The bare HTMLElement
component doesn't have any dependency and creates a
unmatched tiny package.
Prism
is the smallest package for a single component. Absolutely
no runtime or dependency. Very impressive.
GitHub's Catalyst
is an impressive tiny package but doesn't
have a template rendering capability. You may need to add jtml
(from GitHub), lit-html
or uhtml
.
Svelte
is doing also very well with a very small
library runtime cost due to excellent tree-shaking.
Angular 11
, React
, Vue2
and ElemX
libraries go above the 20KB mark.
ElemX
comes with MobX
, it's powerful but it's not light.
Vue3 "One Piece"
is delivering on promises with 30% improvement versus version 2.
The new Lit
is smaller than the previous LitElement
version by almost 20%.
The new Lit
in TypeScript is bigger than the JavaScript due to the use of decorators (They do add quite some boiler plate code after transpilation).
Caution
Some libraries bring significant value and are more feature rich than others. It's normal to see larger sizes in these cases.
Nevertheless, for a simple counter component, we would like to see more tree-shaking happening.
On the other hand, once you start having multiple components using a broad spectrum of the library capabilities, you end up with the entire library anyway.
Estimated Bundle size of 30 components using the same library
This is an estimated size of a bundle of 30 my-counter-like components using the same library. All components will share the library code so the estimated size is calculated with: 1 bundle-with-dependencies + 29x components-without-dependencies.
Notes
It's interesting to see that some libraries managed to be smaller than bare HTMLElement
.
This is achieved by sharing more runtime accross the different components.
Individual components are smaller and pay for the runtime. A library is not
always an overhead !
The new comers Exalt
, htmlhammer
and Synergy
are positioned right on the top. It seems that new libraries are more size concious
right from the beginning.
Prism
is not performing as well here because of the lack of library runtime.
There is nothing to share between components.
Again here, Vue3 "One Piece"
is delivering smaller compiled components
and smaller runtime than Vue2
Performance
Performance of Parsing JavaScript + Create DOM tree
Benchmarked with Tachometer on Chrome Version 90.0.4430.93 (Official Build) (x86_64).
The benchmark page is composed of 50 <my-counter></my-counter>
laid out in a single page with the library bundle code inlined.
Notes
Take into consideration that there is a margin of error of 2ms.
Things that play a role in this performance figure are:
- The size of the JavaScript to parse (more JavaScript = longer).
- The performance of the library to create the DOM nodes from scratch.
Everything runs locally so the network download time is not taken into account here.
Everything runs on a relatively powerful laptop and results could be completely different on a lower-end device like a phone or a tablet.
Final Notes
It's hard to find so much diversity in a technical solution. It tells a lot about Web Components and the low level API provided. All the 55 variants could be on the same page if we wanted to!
On bundle size, the results are already very promising but there is still room for improvement:
- Some libraries would benefit from splitting their features, to better benefit from tree-shaking.
- Except Stencil and Svelte, none of the libraries offer CSS minification out of the box.
Web Components are well supported in browsers now. Still, for old browsers (including IE11), polyfills are required. The bundle sizes reported here are only for modern browsers that don't need any polyfills. It was too much additional work to include polyfills in this post. Maybe later... before they become obsolete.
We will report on new libraries and updates. Make sure to follow us on Twitter.
Feedback or Questions
Chat with us on our Discord or Twitter channels.
Thanks
Thanks to all those who helped us review and correct this analysis.- @Uppercod creator of Atomico
- @adamdbradley creator of Stencil
- @matthewcp creator of Haunted
- @eavichay creator of SlimJs, Ottavino and Neow (in alpha)
- @WebReflection creator of Heresy, HyperHTML, lighterhtml, uhtml, uce and uce-template
- @smalluban creator of Hybrids
- @_developit creator of Preact
- @KevinJHill @diervo @pmdartus of the LWC team
- @Rich_Harris representing Svelte
- @justinfagnani from lit-html and LitElement team
- @RyanCarniato creator of Solid
- @trusktr creator of @lume/element
- @luwes creator of Swiss
- @Tarnishablec creator of Gallop
- @EisenbergEffect on team FAST
- @dee_bloo creator of Joist
- @iplayitofflegit creator of Readymade
- @ducksoupdev creator of Ficus JS
- @yysun creator of AppRun
- @keithamus contributor on jtml & Catalyst
- @v_smid creator of htmlhammer
- @jleesons creator of Exalt
- @mattdonkin & @davide_v creator of Synergy
- @kaleidawave creator of Prism compiler
- Tabs from @lion/tabs - Thanks @dakmor and ING
- Charts library by Charts.js
Changes since January 2021 release
- All libraries upgraded to @latest
- Added htmlhammer
- Added Exalt
- Added Synergy
- Added Prism compiler
- Upgraded lit next-major to the new Lit (v2.0.0rc)
- Updated uce-template counter with .uce
- Updated Atomico counter
- Updated Readymade counter
- Removed CanJS - compilation issues
Brought to you by Twitter Discord