Use native webcomponents with AlpineJS
npm install alpinejs-web-components!GitHub release (latest by date)
This package is a tiny script who loads the content of a regular HTML file, convert it to a Web Component and make it usable anywhere in your pages with reactivity and logic powered by Alpine.js.
Browser's native webComponents API is also great, but adding logic and reactivity takes a lot of time and efforts.
So why not combining those two technologies and get the best of these two worlds ?
With this package you will be able to create reusable HTML Web Components with Alpine.js logic and reactivity, use scoped styles, predefine generic templates without build phase.
Not using a build phase means that everything is done browser side, which may leads to flash rendering effects and layout shifts.
This package does not pretend to be a replacement for JS frameworks and if you already use a build phase, a server side technology, a static site generator or HTMX in your project, it makes literally no sense to use this package.
More of all the above, this package is still in development and is not (yet) meant to be used in production.
Via tag
Insert the following at the end of the tag:
``html`
Via ESM
Insert the following before the closing tag:`html
// import alpinejs-web-components
import AlpineWebComponent from 'https://cdn.jsdelivr.net/npm/alpinejs-web-components/+esm';
// add AlpineWebComponent to the window object
window.AlpineWebComponent = AlpineWebComponent;
// import Alpine.js
import Alpine from 'https://cdn.jsdelivr.net/npm/alpinejs/+esm';
// add Alpine.js to the window object
window.Alpine = Alpine;
// Start Alpine.js
Alpine.start();
`
Create a .html file and add some add HTML, CSS and JS
`html
`
This file is a regular HTML file, and it would be great if the above js code worked as expected as if.
But this won't work with Web Component.
But if you use Alpine.js and AlpineWebComponents the following will work as expected without additional boilerplate code.
`html
`
After the import of this package and Alpine.js, call the AlpineWebComponent() function.
Via
`
Via ESM (after Alpine.start()) :
`html`
The AlpineWebComponent() function requires 2 arguments :the name of your component
1. .
This will define the HTML tag name for you Web Component.
According to the Web Components specs : Web Component name must start with a lowercase letter and contain a hyphen.
2. the path of your component.
This is the path to your Web Component's HTML file.
You can generate different components with different names from the same Web Component's HTML file.
Your component name and your component HTML file name does not need to be the same.
>NOTE : The rest of this document will use ESM.
Just add your Web Component tag anywhere inside the
`html
`The tag name will be the first argument that you defined in the
AlpineWebComponent() function.>NOTE : Web Components tag can not be auto-closed.
This mean that writing only
will not work.
You have to explicitly open AND close your tag :
even if you have nothing to put between the tags.>REMIND : In order to use Alpine.js you need to set a
x-data directive to your component or to a parent.Your
Web Component is now fully powered by Alpine.js and can consume all Alpine.js methods and directives !
Full code (ESM) :
`html
`
`html
Title
import AlpineWebComponent from 'https://cdn.jsdelivr.net/npm/alpinejs-web-components/+esm';
window.AlpineWebComponent = AlpineWebComponent;
import Alpine from 'https://cdn.jsdelivr.net/npm/alpinejs/+esm';
window.Alpine = Alpine;
Alpine.start();
AlpineWebComponent('alpine-button', '/_components/Button.html');
`
Global imports in a separate js file
You can put all your common JS code and imports in a separated file :
/js/script.js`js
// import alpinejs-web-components
import AlpineWebComponent from 'https://cdn.jsdelivr.net/npm/alpinejs-web-components/+esm';// add alpinejs-web-components to the window object
window.AlpineWebComponent = AlpineWebComponent;
// import Alpine.js
import Alpine from 'https://cdn.jsdelivr.net/npm/alpinejs/+esm';
// add Alpine.js to the window object
window.Alpine = Alpine;
// Start Alpine.js
Alpine.start();
// import directly the common components you want to use on all pages
AlpineWebComponent('alpine-header', '/_components/Header.html');
AlpineWebComponent('alpine-footer', '/_components/Footer.html');
``html
INDEX
`
Shadow DOM & scoped CSS
By default, Web Components generated with
AlpineWebComponent() are returned without a shadow DOM.Add the
shadow attribute to your Web Component tag to add a shadow DOM with mode: "open".`html
`The HTML in Your component will not be accessible by regular scripts.
But it changes nothing for Alpine.js who will work the same with or without shadow DOM.
By adding a shadow DOM to your component, the CSS written inside your Web Component is now fully
scoped to your Web Component HTML.`html
`
More about Web Component and CSS :
The HTML tag you create for your web component is not a regular tag and the browser renders it by default as an
inline element. You can style the component's root tag from the inside by using the
:host CSS pseudo-class.>The
:root pseudo-class (which refers to the document tag) is not available in the shadow DOM.>The
:host pseudo-class (which refers to your Web Component root tag) is only available in the shadow DOM.If you use the
:host pseudo-class in a non-shadow DOM component, the AlpineWebComponent() function will change it to your component tag before appending it to the global DOM
For example if you define an
alpine-button component without Shadow DOM and use :host in your component's CSS,
:host { ... } will be changed to alpine-button { ... }`html
`
will be returned as :`html
`
>NOTE : You cannot use CSS native nesting inside a
:root pseudo-class if your component have a shadow DOM.
> NOTE : Without a shadow DOM, your CSS will be applied globally.
Props
You can pass props (data) to your component just by using regular Alpine.js x-data.`html
``html
`
Will be rendered :`html
I am a global prop
I am a local prop
`
Theses props are just Alpine.js reactive data who can be updated from inside or outside the component.`html
`
`html
`
Slots
Slots works (almost) like regular Web Components slots.
Without shadow DOM, the markup you put inside your Web Component's tags
will be automatically
prepended to your Web Component html.`html
one
two
`
`html
three
four
`Will be returned :
`html
one
two
three
four
`you can use
named slots if you use a shadow DOM or if you want to control where to append the markup.`html
last
first
middle
last
first
middle
``html
one
two
`Will be returned :
`html
first
one
middle
two
last
first
one
middle
two
last
`With the shadow option, the markup used as slot will be appended to the shadow DOM.
They wont be affected by global styles and will be stylable from the component CSS.
`html
span
another span
`
`html
`Will be returned :
`html
bold span
red span
`
Components nesting
You can nest components and, for example, define a Web Component who includes other Web Components.
> NOTE : You cannot define a component (by using the
AlpineWebComponent() function) inside another component.
You need to define all the components in your (parent) index.html >NOTE : Components that have nested components must be without a shadow DOM
but children components can have a shadow DOM.
Layouts
Been able to nest components means that you can define common layout templates and use them in all your pages.
You can use slots to add markup locally.
`html
`Just like nested components, you must define your components in the destination html file or via a common javascript file.
/js/script.js`js
// import alpinejs-web-components
import AlpineWebComponent from 'https://cdn.jsdelivr.net/npm/alpinejs-web-components/+esm';// add alpinejs-web-components to the window object
window.AlpineWebComponent = AlpineWebComponent;
// import Alpine.js
import Alpine from 'https://cdn.jsdelivr.net/npm/alpinejs/+esm';
// add Alpine.js to the window object
window.Alpine = Alpine;
// Start Alpine.js
Alpine.start();
// Define layout
AlpineWebComponent('alpine-layout', '/_layouts/Layout.html');
// Define components used in the layout
AlpineWebComponent('alpine-header', '/_components/Header.html');
AlpineWebComponent('alpine-footer', '/_components/Footer.html');
``html
layout example
Some markup (via slot)
`The
part will be rendered :`html
LAYOUT EXAMPLE
Some markup (via slot)
another markup (via slot)
You can get rid of the slot extra
tag by using a tag instead.Using the previous layout example :
`html
Some markup (via slot)
another markup (via slot)
`
Will be rendered :
`html
LAYOUT EXAMPLE
another markup (via slot)
`
and listen to it from the parent by using Alpine.js x-on / @ event listener.`html
``html
`
Persist state
You can use Alpine.js persist plugin to keep the state of a data across pages, Web Components and even if you close your browser.
`html
``html
`
Third-party libraries in your components
There is 2 ways of using third-party library in your components.
>NOTE : in both cases, the Web Component must be WITHOUT shadow DOM and
the Javascript who initiate the library MUST be written directly inside the Web Component file.
$3
Consider the following main
/index.html file importing Swiper.js library JS and CSS
`html
import Swiper Web Component
`and the following component with the minimum HTML, Javascript and CSS needed for activate a Swiper carousel.
`html
`The javascript inside a Web Component will not be imported and executed on your main
index.html
unless you provide an export attribute to the script tag.`html
`So the final component will look like this :
`html
`$3
Consider the following main
/index.html :
`html
import Swiper Web Component
`and the following component importing Swiper.js library JS and CSS
`html
`As seen previously, the script tags needs an
export attribute to be imported and executed.But the problem is that the javascript who activates the library will be executed before the library itself is fully loaded and ready to use.
To resolve this you can add a value to the export attribute on the script tag who loads the library.
This value (a string) will be converted to a custom event name that will be fired when the library is fully loaded and ready to use.
Then you can use this custom event name to wait before activate the library.
`html
`
Import components just in time (like Astro.js islands)
The HTML inside a
tag is not parsed and rendered until its content is extracted with js and appended to the DOM.
This is what Alpine.js do with if the x-if directive and this is why x-if is only usable with the tag. So you can put your Web Component in a
tag and use Alpine.js x-if and switch a true/false condition according to a certain event or action.This event / action could be :
・ window.onload
・ scroll into view (using Alpine.js Intersect Plugin by example)
・ click on a button
・ a mouse hover
etc.
With the Swiper example it could look like that :
`html
`The js and the css written or imported inside the component will not be downloaded and executed until the
x-if condition is resolved to true.>NOTE : with Alpine.js, a
tag must have only one direct child.
If you need to display more nodes, you will need to wrap them into a