- 1 – It’s an HTML enhancement – a sort of ‘declarative jQuery’
- 2- Not for all kinds of projects
- 3- Alpine.js makes use of Vue’s reactivity system
- 4- Alpine.js is built with the Proxy pattern
- 5- Compared to front-end Vue, Alpine offers a fresher way to make things
- 6 – Alpine.effect allows the creation of DOM reactivity via a jQuery-esque method
- 7- Using Livewire, you get a full-stack reactive framework
- 8- You can fetch and async easily with Alpine
- 9 – x-if and x-show are different
- 10- Alpine is extendible, in a cool way
When developing web applications I like to experiment with the capabilities of the tools the ecosystem has to offer, in order to find the most accurate solution for a particular project and to refine the coolest development stack possible. Every modern web development tool has specific potentials, limitations, and goals. The Alpine.js framework is one of the front-end tools that has most caught my interest; here are ten things I discovered while using this tool for web development.
1 – It’s an HTML enhancement – a sort of ‘declarative jQuery’
Alpine.js is a pretty powerful front-end library that allows users to write responsive interfaces in declarative mode. Typically, Alpine is used to make static HTML templates interactive by augmenting and enhancing the HTML syntax via new directives and Javascript injection. I like to describe Alpine as a kind of ‘declarative jQuery’ (or a modern replacement for jQuery with a Vue template taste), because it allows users to manipulate the DOM in a Vue-like way, but without the need for bundling and installation.
Of course you can install Alpine via NPM and import it into your bundle, making use of the full Alpine Javascript API, but for most projects, importing is made easy by injecting a simple, old-fashioned script derived from a CDN into your app.
Putting Alpine’s script in your <head> gives you the ability to manipulate the DOM in a declarative mode in just a few steps, and with a much shallower learning curve than tools like Angular, React, or Vue demand.
2- Not for all kinds of projects
Alpine is a kind of layer on top of static HTML which enables reactive interactivity to be easily added. Personally, I don’t see Alpine.js as a future competitor to the big three, but I don’t think that’s the authors’ intention; tools like React, Svelte, or Vue are complete development stacks allowing users to manage the web application in its entirety, while Alpine, as previously stated, is a tool for creating interactive front-end interfaces.
In my experience, not all projects need pachydermic, bulky tools like the big three listed, and it’s precisely for the projects that don’t need these tools that Alpine.js is most suitable.
Adding reactivity to a template in a simple and very powerful way without the need for bundles and back-end extensions – that’s the Alpine.js tool in a nutshell.
3- Alpine.js makes use of Vue’s reactivity system
Once you’re accustomed to Alpine’s way of making interactive data, you may think it’s a bit magical.
In reality, if you study the source code of any open-source tool, you’ll find that the ‘magic’ is really very pragmatic. Reactivity in Alpine.js is caused by two main functions – Alpine.reactive () and Alpine.effect () – but if you take a look under the hood, these functions use VueJS’s reactivity engine. This engine is very powerful and can be used as a standalone, or with abstractions built on top of it.
4- Alpine.js is built with the Proxy pattern
If you are interested in Design Pattern architectures, you may find it entertaining that in order to make the magic happen, the Alpine.reactive function makes use of the Proxy pattern.
A Proxy is basically a JavaScript object which allows you to wrap other objects, and to intercept and redefine operations on them. In a nutshell, Proxy objects make it possible to define objects that can be used in place of the original object, but which may redefine fundamental Object operations such as getting, setting, and defining properties, i.e., by using a little bit of Javascript in order to better understand that:
let obj = { color: ‘blue’ }
let proxy = Alpine.reactive(obj)
Code language: JavaScript (javascript)
Here, the function Alpine.reactive receives a plain object and wraps it inside a Proxy object. The “proxy” variable is a wrapper around the “obj” variable, so any attempts to get or set a “proxy” property will behave exactly as if you had interacted with “obj” directly:
console.log(obj.color) // blue
console.log(proxy.color) // blue
proxy.color = ‘green’
console.log(obj.color) // green
Code language: JavaScript (javascript)
Now, any time you update or retrieve a value from the proxy object, Alpine is aware of this and can run any other logic that depends on this data.
5- Compared to front-end Vue, Alpine offers a fresher way to make things
There are many similarities between Alpine and (front-end) Vue, however, I personally find the Alpine approach a more comfortable option for developing interfaces – and it’s a bit cooler as a development experience.
For example:
1- in Alpine (in most cases) you do not need a <template> wrapper
2- Alpine allows you to wrap component data directly in HTML, enhancing its functionalities (no need for separation of concerns):
div x-data = "{isOpen: false}">
<button x-on:click ="isOpen = !isOpen">Toggle</button>
<h1 x-show = "isOpen">Alpine.js HTML enhancement!</h1>
Code language: JavaScript (javascript)
3- if you’re worried about separation of concerns and you want to reuse your component logic, this is possible without the need for export logic, a “method” object, or returning a data() wrapper:
function component1Data() {
return {
isOpen: false,
toggleOpen() { this.isOpen = !this.isOpen }
}
}
<div x-data = "component1Data()">
Code language: JavaScript (javascript)
6 – Alpine.effect allows the creation of DOM reactivity via a jQuery-esque method
You can make powerful use of the second reactivity-maker function of Alpine – Alpine.reactive – which makes any data reactive. This works in combination with the previous reactivity maker:
let obj = { color: ‘blue’ }
let proxy = Alpine.reactive(obj)
Alpine.effect(() => {
console.log(proxy.color)
})
Code language: JavaScript (javascript)
With this approach, the callback will be executed once, and whenever you interact with the reactive “color” property of the proxy object you will console.log its value.
This means you can skip the Alpine-HTML template completely and rely on vanilla Javascript DOM manipulations and the two Alpine reactivity-maker functions:
let button = document.querySelector('button')
let span = document.querySelector('span')
let proxy = Alpine.reactive({ color: ‘blue’ })
Alpine.effect(() => {
span.textContent = proxy.color
})
button.addEventListener('click', () => {
proxy.color = (proxy.color == ‘blue’) ? ‘green’ : ‘blue’
})
Code language: JavaScript (javascript)
This feels to me like a fresh, yet powerful combination of imperative-declarative paradigm and a jQuery-Vue approach to doing things.
7- Using Livewire, you get a full-stack reactive framework
It’s no secret that I love Laravel, and I like Livewire too. Alpine.js seems to me to be a perfect companion for Livewire, producing results that resemble the full-stack React/Vue experience of making web applications using a PHP environment. You can do many pretty amazing things with these two pieces of code straight out of the box – the most basic option is the interaction between a server-state and a front-end template:
class Obj1 extends Component
{
public $color = ‘blue’;
public function toggle()
{
$this->color = ( $this->color == ‘blue’ ) ? ‘green’ : ‘blue’;
}
}
<div>
<div x-data>
<h1 x-text="$wire.color"></h1>
<button x-on:click="$wire.toggle()">Toggle color</button>
</div>
</div>
Code language: PHP (php)
Because the $wire variable also uses a JavaScript Proxy under the hood, users can access properties and call methods on it, with those operations forwarded to Livewire.
8- You can fetch and async easily with Alpine
Although Alpine is not designed as a native full-stack interaction tool, asynchronous operations that involve fetching data from a server are very common, and also very important from a templating perspective. You can fetch data very easily directly in the Alpine template – either by explicitly marking a function handler as async, or the opposite. In the latter case, Alpine will do the work for you:
async function getItems() {
let response = await fetch('/api/items/all')
let json = await response.json()
return json
}
<div x-text="JSON.stringify(getItems())"></div>
or alternatively:
<div
x-data="{
items: {},
async getItems() {
this.items = await (await fetch('/api/items/all')).json();
}
}"
x-init="getItems">
</div>
Code language: PHP (php)
9 – x-if and x-show are different
This is quite a simple, but key concept: Alpine.js shines when it comes to toggle elements, a very common operation in making every frontend template. Here you have two ways of doing this: the x-show and x-if directives. They may look similar, but they are actually very different.
First of all, the x-show directive ‘hides’ or ‘shows’ a DOM element, i.e., when you don’t see the element, it’s because its CSS display property is set to ‘none’, while x-if completely removes the element from DOM.
Secondly, because of this behaviour, the element (or a series of elements nested in the root element) you have to inject the x-if directly into a <template> wrapper rather than into the direct DOM element you want to toggle.
Finally, x-show supports transition toggling, meaning you can combine its functionalities with the x-transition directive, which you can’t do with x-if. Here you have two distinct, powerful mechanisms which allow you to toggle template parts with full control over the operation.
10- Alpine is extendible, in a cool way
You are not bound to the directives and functionalities you’ll find in the Alpine core engine. Instead, users can extend that mechanism by using the Alpine.directive and Alpine.magic functions. The first of these allows you to make custom directives that can then be injected into the HTML template:
Alpine.directive('wrap', el => {
el.textContent = ‘***’ + el.textContent + ‘***’
})
<div x-data>
<span x-wrap>Wrap this text! </span>
</div>
Code language: HTML, XML (xml)
The second allows you to make magic properties and functions:
Alpine.magic('alert', () => {
return subject => alert(subject)
})
<button @click="$alert('say hello!')">Alert this element text content</button>
Code language: PHP (php)