"𝙎𝙢𝙖𝙡𝙡𝙚𝙨𝙩𝙚𝙙𝙞𝙣𝙜" : making a Web Component 29% smaller
Tags: webcomponents, webdev, javascript, showdev
Published: 2021-07-11 12:14:22

---
title: "𝙎𝙢𝙖𝙡𝙡𝙚𝙨𝙩𝙚𝙙𝙞𝙣𝙜" : making a Web Component 29% smaller
published: true
description: Refactor the &lt;my-counter> Web Component to a smaller (and faster) version
tags: WebComponents,WebDev,JavaScript,showDev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hn7fn4mx49b2dj9x6tgw.jpg
---

The [WebComponents.Dev](https://webcomponents.dev) site blogs about [51 ways/languages to make a ``<my-counter>`` Web Component](https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/)

Native code ranks first in the [File Size Ranking](https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/#performance)

![](https://i.imgur.com/As1HzLt.png)

But... I am totally against comparison with Svelte, because the Svelte compiler _optimizes_ code

So if I _"play compiler"_

That native HTMLElement code can/may be optimized 
(and so can all other source versions)

| |gzip|
|---|---|---|
|original by WebComponents DEV|&nbsp;476 B*|
|Svelte|1884 B
|refactored|&nbsp;355 B - savings: **25%** |
|optimized|&nbsp;339 B - savings: **29%** |

*) The Web Components DEV site says the file is 505 Bytes, 
copied to GitHub.io my [``<file-size>`` Web Component](https://dev.to/dannyengelman/file-size-web-component-because-size-does-matter-3d3k) reports it is 476 Bytes.

<hr>

# Original &lt;my-counter> = 476 Bytes:

{% jsfiddle https://jsfiddle.net/WebComponents/zwsptrvx result,html,js,css %}

# Refactor code

See ``JavaScript`` Tab in above JSFiddle.

* Template literals are great, but they suck up bytes, as **meaningless** white space and ``\n`` newlines are still included in the minified file.

* no need for a ``createElement('template')`` when we only want the innerHTML **once** 

* template/content should **not** be **added** in the connectedCallback (as it can run multiple times)

* ``super()`` sets **and** returns the _this_ scope
* ``attachShadow()`` sets **and** returns ``this.shadowRoot``
* so everything can be chained:

    ```javascript
        constructor() {
            super()
                .attachShadow({ mode: 'open' })
                .innerHTML = "<style>*{font-size:200%}...
    ```

* no need for a MyCounter class definition when it is used only once 

    ```javascript
        customElements.define('my-counter', class extends HTMLElement {}
    ```

* Nearly all of the 51 examples use inline event handlers (notation). 
    ```javascript
        render() {
            return html`
            <button @click="${this.dec}">-</button>
            <span>${this.count}</span>
            <button @click="${this.inc}">+</button>
            `;
        }
    ```

* Then we can do that as well

   * we have to add extra code to find the **``inc()``** and **``dec()``** methods on the element (which libraries do for you under the hood)
   * the ``id`` references on the buttons are no longer needed

    ```html
        <button onclick="this.getRootNode().host.inc()">
        <button onclick="this.getRootNode().host.dec()">
    ```

* The Component uses shadowRoot to encapsulate styles **and content**. The  ``id`` on ``<span id="count">`` is not required because we can target the only ``<span>`` that exists in shadowDOM

* All the refactored ``connectedCallback`` does is set the span innerHTML to 0
    ```javascript
        connectedCallback() {
            this.update(this.count);
        }
    ```

    Set the default **0** in HTML, and the connectedCallback **is no longer required**

    ```html
        "<span>0</span>"+
    ```

* remove not required white space and ; from CSS
* remove not required quotes from HTML attributes, because the Browser will add them 
    ```html
        <button onclick=this.getRootNode().host.inc()>
        <button onclick=this.getRootNode().host.dec()>
    ```

<hr>

# Refactored code = 355 Bytes:

```javascript
customElements.define("my-counter", class extends HTMLElement {
    constructor() {
      super()
        .attachShadow({ mode: "open" })
        .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 onclick=this.getRootNode().host.dec()>-</button>" +
        "<span>0</span>" +
        "<button onclick=this.getRootNode().host.inc()>+</button>";
      this.count = 0;
    }
    inc() {
      this.update(++this.count);
    }
    dec() {
      this.update(--this.count);
    }
    update(count) {
      this.shadowRoot.querySelector("span").innerHTML = count;
    }
  }
);
```

{% jsfiddle https://jsfiddle.net/WebComponents/n8pgL59q result,html,js,css %}

<hr>

# Optimized code = 339 Bytes:

This Component can be made better and even smaller

* The ``inc``, ``dec`` and ``update`` **methods** are not required, when ``count`` is made a **getter/setter**

* DRY (Don't Repeated Yourself) is great from a code maintenance Point-of-View. But from a delivery and performance PoV you do **not** want to be DRY; **GZip loves repetitions** 
  ``* {font-size:200%}`` is applied to 2 elements only (button and span) 
  Setting ``font-size:200%`` on **both** elements creates a **larger** file, but a **smaller** GZipped file! 
  (And the CSS parser has less work to do)

* ``.count-- >`` needs that extra space, to close the ``onclick`` definition, or the minifier will add an ``-`` escape code, adding 4 bytes.

* ``<span>`` can be replaced with ``<p>``

* no ``this.count = 0;`` required because ``<p>0</p>`` **is** the state

* replacing ``seagreen`` and ``white`` with shorter ``#xxx`` notation doesn't save extra bytes in this case, because the **#** doesn't exist yet in the code, thus requires extra GZip encoding bits.

```javascript
customElements.define(
  "my-counter",
  class extends HTMLElement {
    constructor() {
      super().attachShadow({
        mode: "open",
      }).innerHTML =
        "<style>" +
        "p{font-size:200%;width:4rem;display:inline-block;text-align:center}" +
        "button{font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white}" +
        "</style>" +
        "<button onclick=this.getRootNode().host.count-- >-</button>" +
        "<p>0</p>" +
        "<button onclick=this.getRootNode().host.count++>+</button>";
    }
    set count(p) {
      this.shadowRoot.querySelector("p").innerHTML = p;
    }
    get count() {
      return ~~this.shadowRoot.querySelector("p").innerHTML;
    }
  }
);
```  

{% jsfiddle https://jsfiddle.net/WebComponents/h3yLbf1k result,html,js,css %}

Note: You can save 6 more bytes using _**INline styles**_ 
but code maintainability suffers:

```javascript
.innerHTML = 
"<button style=font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white onclick=this.getRootNode().host.count-- >-</button>" +
"<p style=font-size:200%;width:4rem;display:inline-block;text-align:center>0</p>" +
"<button style=font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white onclick=this.getRootNode().host.count++>+</button>"
```

<hr>

# _smallesteding-ed_ Conclusion

Refactoring Web Components makes them smaller and better.

| |gzip|
|---|---|---|
|[original by WebComponents.DEV](https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/#source-code)|&nbsp;476 B*|
|Svelte|1884 B
|refactored|&nbsp;355 B - savings: **25%** |
|optimized|&nbsp;339 B - savings: **29%** |



<br>
<hr>
<br>

{% jsfiddle https://jsfiddle.net/WebComponents/62f1eLay result,html,js %}