Code-splitting and Lazy-loading Alpine Components using Async Alpine
June 2, 2023
//3 min read
Code-splitting and Lazy-loading Alpine Components using Async Alpine
With AlpineJs, you can create components in two ways: using inline HTML markup or the Alpine.data()
function to define a component globally and use it throughout the entire project.
Each approach has its pros and cons. For example, It is not possible to cache the inline components. And are only reusable if you use your template engine to create reusable components. The benefit of this approach is that you don't need a build step; you can add the AlpineJS to your HTML markup using CDN and start building your application.
On the other hand, using Alpine.data()
to declare the components, you can reuse them and cache them like other static files. This option has some downsides too.
You must set up a bundler like Vite or Webpack to bundle the codes. And if you have a big project, your bundled file size can grow over time.
To fix these issues, you can use Async Alpine to split your components into individual files and load them only when needed.
Installation
You can use Async Alpine using CDN or npm:
1<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/async-alpine.script.js"></script>
When you want to use CDN, you must load Async Alpine before loading the Alpine itself.
Install from npm with:
1npm install async-alpine
Import it into your bundle alongside Alpine and run AsyncAlpine.init(Alpine) and AsyncAlpine.start() before Alpine.start():
1import AsyncAlpine from 'async-alpine';2import Alpine from 'alpinejs';3AsyncAlpine.init(Alpine);4AsyncAlpine.start();5Alpine.start();
Usage
To use Async Alpine, you must create your components in individual files using ES Module format like the code below:
1// publicly available at `/assets/path-to-component.js`2export default function myComponent() {3 return {4 message: '',5 init() {6 this.message = 'my component has initialised!'7 }8 }9}
After creating components in separate files, you can load them using one of these four options depending on your environment: URL Components — most straightforward implementation declaring component URLs in JS; Data Components — for building tools with dynamic import/code-splitting like Vite, WebPack, Rollup, and Parcel; Alias Loading — if your components are in a very consistent file-system structure; Inline Components — you need to declare components in HTML to get the asset URL, for example, using an asset CDN or for Shopify sites.
After loading components, you can use them asynchronously by adding x-ignore
and ax-load
with your x-data
attribute like the code below:
1<div2 x-ignore3 ax-load="visible"4 x-data="myComponent"5>6 <div x-text="message"></div>7</div>
Loading Strategies
In the code above, you can see that we used a value in the ax-load
attribute. You can change the loading strategy by setting these values in the ax-load
Eager
It's the default behavior; if you do not provide any value, the component will load in this mode. In eager mode, the component will start loading immediately on page load. It will still load asynchronously in the background but with the highest priority possible.
This mode is useful when you want a component to be interactive as soon as possible.
Idle
It uses requestIdleCallback, where it's supported to load when the main thread is less busy. Components not critical to the initial load are bests for this scenario.
Visible
It uses IntersectionObserver only to load when the component is in view, similar to lazy-loading images.
Media
The component will be loaded when the provided media query is true.
Event
The component will be loaded once it receives the async-alpine:load event on the window. Provide the id of the component in detail.id.
1<!-- on a button click using Alpine's $dispatch --> 2<button x-data @click="$dispatch('async-alpine:load', { id: 'my-component-1' })">Load component</button> 3<div 4 id="my-component-1" 5 x-ignore 6 ax-load="event" 7 x-data="componentName" 8></div> 9 10<!-- load our component after `another-library-init` has loaded -->11<script>12window.addEventListener('another-library-init', () => {13 window.dispatchEvent(new CustomEvent('async-alpine:load', {14 detail: {15 id: 'my-component-2'16 }17 }))18})19</script>20<div21 id="my-component-2"22 x-ignore23 ax-load="media (prefers-reduced-motion: no-preference)"24 x-data="componentName"25></div>
For more information and more detailed examples, you can check out the official document of Async Alpine here.