Crear WebComponents con StencilJS y reutilizarlos en diferentes proyectos

Me ha pasado en varias ocasiones la necesidad de re-utilizar un componente en varios proyectos. ¿La solución? Copiar-pegar el componente y llevármelo al proyecto que lo necesite, y tal vez, ajustar alguna cosa para que funcione. ¿Te suena?

Tal vez esto te funciona cuando el componente en cuestión, por ejemplo un botón, te lo llevas de un proyecto React a otro proyecto React, pero… ¿qué haces si un proyecto es de React, otro de Angular u otro de Vue? Tocaría re-hacer ese botón para cada proyecto. Es decir, estarás haciendo tres veces la misma funcionalidad. No suena bien, ¿no? 💩

Bueno, si te sientes identificado/a con ésta situación, me alegro de no ser el único.

¿Qué tal te suena si hacemos ese mismo botón una sóla vez y lo aprovechamos para nuestros proyectos? Venga, vamos a ello.

Índice de contenidos

  • Introducción
  • ¿Qué es StencilJS?
  • Crear un proyecto con StencilJS
  • Creación de un componente botón reusable con StencilJS
  • Añadir estilos CSS a un webcomponent
  • ¿Cómo ver un webcompoent creado con Stencil?
  • Generando la distribución
  • Distribuir nuestro webcomponent en un package de Github
  • Integración en un proyecto con React
  • Integración en un proyecto con Vue
  • Conclusiones

Introducción

En este artículo vamos a ver con un ejemplo práctico cómo implementar un componente con StencilJS (el botón comentado anteriormente), lo publicaremos como un packageen Github y luego lo integraremos en nuestras aplicaciones, haremos una prueba de integración en Vue y otro en React. ¿Suena bien? ¡Al lío!

¿Qué es StencilJS?

StencilJS es una herramienta de los creadores de Ionic, que definen como «El generador mágico de web components reusables». Es decir, StencilJS permite crear de forma sencilla y sin grandes esfuerzos web components nativos.

Crear un proyecto con StencilJS

Lo primero que haremos, como es obvio, crear el proyecto, muy fácil:

npm init stencil

Lo que automáticamente nos preguntará que tipo de proyecto es:

Tipo de proyecto Stencil

Nos salen tres opciones de tipos de proyecto. Vamos a seleccionar la opción component ya que lo que queremos crear es una colección de componentes. Como nombre del proyecto pondremos custom-components-stencil (por ejemplo):

Confirmación de la instalación del proyecto con Stencil

Perfecto, ya hemos creado nuestro primer proyecto con Stencil 🚀, ahora abrimos el proyecto con nuestro IDE favorito y empezamos:

cd custom-components-stencil
npm install
npm start

Instalación de dependencias y ejecutamos el proyecto

Bien, una vez arrancado el proyecto nos dirá la URL en la que está funcionando nuestro ejemplo, en mi caso en http://localhost:3333/ donde veremos un ejemplo básico que nos ofrece por defecto StencilJS.

Creación de un componente botón reusable con StencilJS

Llegados a este punto tenemos ya instalado nuestro primer proyecto StencilJS. Ahora vamos a crear nuestro primer webcomponent de tipo botón reusable.

Lo primero, nos fijaremos en el archivo src/index.html donde veremos que Stencil ya nos ofrece un ejemplo de uso de un componente llamado my-component, pégale un vistazo a la carpeta src/components/my-component que es donde está definido. Básicamente es un div que recibe tres props (first, middle, last) y los pinta por pantalla.

Bien, vamos a crear nuestro componente. Seguiremos los siguientes pasos:

  1. Creamos a carpeta dentro de src que llamaremos at-button (at es de Alex Tomás 😜)
  2. Dentro de src/components/at-button creamos un archivo con formato .tsx que llamaremos at-button.tsx ya añadimos lo siguiente:
import { Component, Prop, h } from '@stencil/core';

@Component({
    tag: 'at-button',
    styleUrl: 'at-button.css',
    shadow: true,
})
export class ATButton {
    @Prop() text: string;

    render() {
        return <button class='button'>{this.text}</button>;
    }
}

Bueno, bueno, esto son muchas cosas, pero que no cunda el pánico. Veamos cuales son las partes del componente:

  • Decorador @Component es donde especificamos el nombre del componente y su fichero de estilos.
  • La @Prop text va a ser la propiedad que le enviaremos al componente. Estas propiedades por defecto son inmutables desde dentro del componente. Para hacerlas mutables hay que anotarlas con @Prop({ mutable: true }), pero no va a ser el caso.
  • El método render, que devuelve la descripción necesaria para pintar el componente. Cómo verás usamos sintaxis JSX. Si no conoces JSX pásate por este enlace de la documentación de React que explica qué es JSX.

Recuerda crear el fichero de estilos en src/components/at-button/at-button.css

Añadir estilos CSS a un webcomponent

Muy sencillo. Al añadir en el decorador @Component el nombre del archivo CSS, añadimos las clases que queramos. En nuestro caso le hemos añadido la clase button a nuestro botón. Por lo tanto, le añadimos un estilo básico (recuerda que estamos aprendiendo la funcionalidad de los webcomponentes, no en la estética 🙃):

.button {
    background-color: teal;
    border-radius: 30px;
    border: none;
    box-shadow: 2px 2px 9px 2px rgba(0, 0, 0, 0.28);
    cursor: pointer;
    padding: 10px 30px;
    color: white;
}
.button:hover {
    background-color: rgb(0, 146, 146);
}

Vale, perfecto. ¿En qué punto estamos?

  1. Hemos iniciado el proyecto con Stencil.
  2. Tenemos un webcomponent creado que se llama at-button.
  3. Le hemos dado estilos css.

¿Y ahora como lo puedo ver?

¿Cómo ver un webcompoent creado con Stencil?

Recuerda, que al principio del artículo, al crear el proyecto teníamos el comando npm run start o npm start para ver el proyecto en nuestro navegador. Vale, pero antes vamos a editar el fichero src/index.html y lo dejaremos de la siguiente manera:

<!DOCTYPE html>
<html dir="ltr" lang="en">
    <head>
        <meta charset="utf-8" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
        />
        <title>Stencil Component Starter</title>

        <script
            type="module"
            src="/build/custom-components-stencil.esm.js"
        ></script>
        <script nomodule src="/build/custom-components-stencil.js"></script>
    </head>
    <body>
        <at-button text="¡Hola mundo!"></at-button>
    </body>
</html>

Si vamos a nuestro localhost que nos indica Stencil al arrancar el proyecto deberíamos ver nuestro botón:

Webcomponent Hola Mundo

Generando la distribución

¡Ya tienes tu primer webcomponent creado con StencilJS! ¿Y hora como lo vamos a usar en otros proyectos? Lo primero que tenemos que hacer, es compilar todo:

npm run build

Lo que te debe generar la carpeta dist y loader. Perfecto, vamos a crear nuestro packageen Github.

Distribuir nuestro webcomponent en un package de Github

Lo primero que necesitaremos será tener creado un repositorio, si no lo tienes creado, hazlo (no voy a detenerme en como crear un repo y en subir los cambios en él ya que no es el objeto de este artículo 😌).

En mi caso el proyecto estará público en la siguiente URL: https://github.com/alextomas80/stencil-webcomponents

Perfecto. Ya tengo mi proyecto compilado y subido a Github. Ahora vamos a crear nuestro package.

Generar token en Github para crear un package

Aquí vien un poco de magia. Lo primero que necesitamos es un archivo llamado .npmrc donde añadiremos lo siguiente:

//npm.pkg.github.com/:_authToken=TU_TOKEN
registry=https://npm.pkg.github.com/USUARIO

En mi caso será:

//npm.pkg.github.com/:_authToken=d7a031caf35....🤫
registry=https://npm.pkg.github.com/alextomas80

Para crear un token para publicar un package en tu repositorio necesitarás acceder en Github a Settings > Developer settings > Personal access tokens

Crear un token personal en Github

Y necesitaremos hacer un cambio en nuestro package.json:

{
    "name": "custom-components-stencil"
}

Lo cambiaremos por nuestro usuario y nombre que le hayas puesto a tu repositorio, en mi caso quedaría de la siguiente forma:

{
    "name": "@alextomas80/stencil-webcomponents"
}

Bien, tenemos todo listo, ahora sí, para enviar nuestro package a Github 🤞🏻

npm publish --access public

Y deberíamos de obtener una respuesta similar a ésta:

Package subido a Github

¡Sí! ¡Ahora sí! Tienes tu package creado con éxito. ¿Cómo comprobarlo? Accede a la URL de tu repositorio terminado en packages: https://github.com/alextomas80/stencil-webcomponents/packages

Package subido a Github

Integración en un proyecto con React

Es hora de ver que todo esto ha servido para algo y funciona. Vamos a utilizar nuestro webcomponent at-button en un proyecto que vamos a crear con react de una forma rápida. No me voy a entretener mucho con esto:

Creamos nuestro proyecto de ejemplo:

npx create-react-app test-with-react

Arrancamos el proyecto y veremos la pantalla de bienvenida típica de React:

npm run start

Pantalla inicio de React

Ahora instalemos nuestro package con nuestro webcomponent. Si volvemos a Github y consultamos nuestro package veremos que nos indica como podemos instalarlo.

Package subido a Github

Así que usaremos NPM para ello:

npm install @alextomas80/stencil-webcomponents@0.0.1

Bien. Ahora vamos a usarlo. Para ello vamos a pegar un vistazo a la documentación de Stencil para la integración con React, es muy sencillo, yo te lo resumo:

Abrimos el archivo src/index.js y lo dejamos de la siguiente manera:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

// Importamos nuestro package que hemos creado en los anteriores pasos
import {
    applyPolyfills,
    defineCustomElements,
} from '@alextomas80/stencil-webcomponents/loader';

ReactDOM.render(<App />, document.getElementById('root'));

applyPolyfills().then(() => {
    defineCustomElements();
});

Y ahora sí, por fin, vamos a usar y ver nuestro at-button. Abrímos el archivo src/App.js y añadimos nuestro botón de la siguiente manera:

<at-button text="Mi WebComponent en React" />

Con lo que veremos, por fín, nuestro botón de la siguiente manera y estilo:

Usando webcomponent personalizado en React

¡Bien! Como verás tiene el mismo estilo que le dimos al componente cuando lo creamos con Stencil. Y además le podemos pasar la propiedad text para definir el texto que tendrá el botón. Una maravilla. Pero… ¿no habíamos dicho que el fin de este artículo es el de crear un componente y usarlo en React y Vue?

Integración en un proyecto con Vue

Creamos nuestro proyecto de ejemplo Vue:

vue create test-with-vue

Seleccionamos las opciones que nos pide (versión de Vue, etc etc, no me voy a entretener en esta parte). Arrancamos el proyecto y veremos la pantalla de bienvenida típica de Vue:

npm run serve

Bienvenido a VUe

Y vamos a hacer exactamente lo mismo que con ejemplo de React. Vamos a instalar nuestro package con NPM:

npm install @alextomas80/stencil-webcomponents@0.0.1

Vamos a pegar un vistazo de nuevo a la documentación de Stencil para la integración con Vue, también es muy fácil y muy similar a React:

Abrimos el archivo src/main.js y lo dejamos de la siguiente manera:

import { createApp } from 'vue';
import App from './App.vue';

// Importamos nuestro package que hemos creado en los anteriores pasos
import {
    applyPolyfills,
    defineCustomElements,
} from '@alextomas80/stencil-webcomponents/loader';

applyPolyfills().then(() => {
    defineCustomElements();
});

createApp(App).mount('#app');

Y ya podemos usarlo. Para ello vamos al archivo del ejemplo src/App.vue y añadimos:

<at-button text="Mi WebComponent en Vue" />

Usando webcomponent personalizado en Vue

¡Y ahí tenemos de nuevo exactamente el mismo botón!

WebComponents - Pura magia

Conclusiones

Con este sencillo ejemplo has podido ver y probar el potencial que nos ofrece Stencil y las posibilidades que nos brinda. Podemos crear componentes rápidamente y completamente reutilizables en cualquier tipo de aplicación ya sea de una u otra tecnología, o incluso sin que tengan uns framework JavaScript por detrás como hemos hecho.

Así pues, si lo que quieres es tener tu libraría de componentes, compartirla con diferentes proyectos y además trabajar en ellos diferentes compañeros de equipo… te recomiendo totalmente su uso.

Y esto es todo. Espero que te pueda servir.

Si te gusta mi contenido, puedes "invitarme" a un café ☕️ para apoyarme. 👇🏻

¿Me invitas a un café?