How to create a basic web component ?
Published: 2023-03-19 11:19:29

Recently for my job, i have to work with the web components. By that, i had to learn how create web component that i share with you with the example of basic button.

Firstly, why do we want to use web components ? 

The main reason is the web component is generic. You can integrate it on each frontend framework or just basic html page. So if you use a micro-frontend strategy you can have many different frontend framework by micro-frontend. So if you use web components you don't need to create frontend library by frontend framework like React, Vue or Angular but just use your web components library.

Now, place to example:

To create a web component you need a javascript class for it.


```js
class Button extends HTMLElement {
    constructor() {
        super();

        // Create a shadow root
        const shadow = this.attachShadow({ mode: 'open' });

        // Create the main element of the web component
        const buttonElement = document.createElement('button');
        buttonElement.textContent = this.getAttribute('label');

        // Attach the created element to the shadow DOM
        shadow.appendChild(buttonElement);
    }
}

customElements.define('c-button', Button)
```

This class to have extends HTMLElement or children of HTMLElement like HTMLParagraphElement.

All functionalities of your component need to be inside the constructor after the call of super().

You must create the shadow dom for your component.

`const shadow = this.attachShadow({ mode: 'open' });`

It permit to encapsulate your component of the page. By example, the style integration will be interpreted only by your component.

Once the shadow attached you can create your element classicly with `document.createElement('Button')` and add the content of the label attribute added on your web component to this element.

After you just have to attach your button to the shadow dom.
`shadow.appendChild(buttonElement);`

For your component be usable in your dom you have to register your web component by the controller of custom elements [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry).
For that, use its `define` method with name of your web component and the class associated.

`customElements.define('c-button', Button)`

You need a last step to used your web component. You have to add call of your javascript class in the end of the body page.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>How create basic button web component ?</title>
</head>
<body>
    <c-button label="Submit"></c-button>
    <script src="components/button/Button.js"></script>
</body>
</html>
```

Great job you created your first web component ! 🎉 

## Adding style

Now we can add configurable style on our component ! 

For our example, we add classic configurable styles like success, danger, warning and rounded state. We will use attributes to circulate this information and add css classes if theses attributes exist.

```js
// Match attribute style with classes css
const color = this.getAttribute('color')
if (color === 'success') {
    this.buttonElement.classList.add('button--success')
}
if (color === 'danger') {
    this.buttonElement.classList.add('button--danger')
}
if (color === 'warning') {
    this.buttonElement.classList.add('button--warning')
}

const variant = this.getAttribute('variant')
if (variant === 'rounded') {
    this.buttonElement.classList.add('button--rounded')
}
```

Create css file and add your style.

```css
button {
    padding: 0.375rem 0.75rem;
    border: none;
    font-size: 1rem;
}

button:hover {
    cursor: pointer;
}

.button--rounded {
    border-radius: 0.25rem;
}

.button--success {
    background: #188803;
    color: white;
}

.button--success:hover {
    background: #147303FF;
}

.button--danger {
    background: #c61112;
    color: white;
}

.button--danger:hover {
    background: #A20C0DFF;
}

.button--warning {
    background: #ffc107;
    color: black;
}

.button--warning:hover {
    background: #D7A204FF;
}
```

Add link tag to call your css in your web component.

```js
const linkElement = document.createElement("link");
linkElement.setAttribute("rel", "stylesheet");
linkElement.setAttribute("href", "components/button/Button.css");
shadow.appendChild(linkElement);
```

Now you can use your web component with attributes which change its design.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>How create basic button web component ?</title>
</head>
<body>
    <fieldset>
        <legend>Original</legend>
        <button>Submit</button>
    </fieldset>

    <fieldset>
        <legend>WebComponent</legend>
        <c-button label="Normal"></c-button>
        <c-button label="Rounded" variant="rounded"></c-button>
        <c-button label="Success" color="success"></c-button>
        <c-button label="Danger" color="danger" variant="rounded"></c-button>
        <c-button label="Warning" color="warning" variant="rounded"></c-button>
    </fieldset>

    <!-- we already need to put integration component at the end of body -->
    <script src="components/button/Button.js"></script>
</body>
</html>

```

**Illustration**

![Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8km9vj99pb1gocw12i29.png)


## Refresh our component

To refresh the web component from an attribute modification. We need to declare attributes you want observable with the method `observedAttributes`. In our example we want observable variant and color attributes. So we return an array with `color` and `variant` data.

```js
static get observedAttributes() { return ['color', 'variant']; }
```

After you need call your refresh style with the lifecycle callback `attributeChangedCallback(name, oldValue, newValue)` which invoked each time one of the custom element's attributes is added, removed, or changed.

```js
 attributeChangedCallback(name, oldValue, newValue) {
    // Update your style here
 }
```

You can find the complete source code on GitHub : https://github.com/TonyDeplanque/web-component-demo

Documentation:
- https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements