Home / Articles / Introducing AlpineJs. The Ideal jQuery Replacement

Introducing AlpineJs. The Ideal jQuery Replacement

November 15, 2020
6 min. read

Most uncomplicated today web sites, like blogs or landing pages, do not need extensive javascript frameworks with complicated build steps.

Let’s take, for example, the Virtual-DOM concept that is so popular today. Do you need this abstraction to use it in a form or add some simple user interactions for most web sites?

JQuery took this role in the websites, and it is one of the reasons that it is still one of the most popular libraries. Unfortunately, jQuery is a child of an old era and does not consider modern concepts such as the reactive state. Furthermore, most jQuery features, such as selectors or simplified AJAX calls, are replaced by modern javascript.

The question to jQuery replacement for most web sites’ simple logic tasks can answer the Alpine.js framework. It substitutes the jQuery query-based (imperative) approach with the tailwindcss-inspired declarative approach on the DOM using a familiar Vue-like syntax.


The installation is simple enough; add a script to the end of your <head> section:

<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>

The cost of the library itself is low. The comparison with the other frameworks shows that:

Alpine.js Example

Let’s introduce ourselves to this framework by using, as an example, a simple login form. The user provides the username and password, and if the supplied password is equal to pass, then a welcome message is displayed tailored to the given username. Otherwise, an error message is displayed.

The whole code is displayed here:

 1<div x-data="{ username:'', password: '', login : false, error: false }">
 2    <form x-show="!login" x-on:submit.prevent="error=false;
 3        if (password === 'pass') 
 4        login = true; 
 5        else error = true;" method="post">
 6        <div>
 7            <label for="username"><b>Username:</b></label>
 8            <input x-model="username" type="text" placeholder="Enter Username" name="username" required />
 9        </div>
10        <div>
11            <label for="password"><b>Password: </b></label>
12            <input x-model="password" type="password" placeholder="Enter Password" name="password" required />
13        </div>
14        <div>
15            <button type="submit">Login</button>
16        </div>
17        <div>
18            <label>
19                <input type="checkbox" checked="checked" name="remember" /> Remember
20                me
21            </label>
22        </div>
23    </form>
24    <div x-show="login" x-text="`welcome ${username}`"></div>
25    <div x-show="!login && error" style="color: red;">Login failed!</div>

Component Initialization

Take note of the following code:

1<div x-data="{ username:'', password: '', login : false, error: false }">

In the above line, we initialize a component with the corresponding data object. Specifically, we initialize an empty username and password string and set the login and error as false.

The x-data attribute plays a similar role to a Vue’s component data property. Accordingly, these variables are reactive, as you will expect from the Vue.js experience.

Take note that if you seek something like the mounted() in VueJS or the ngOnInit() in Angular hooks, the x-init attribute is more appropriate.


The next step involves the approaches to the variable binding. The following code binds the variable username to the input element’s value using the x-model attribute.

7<input x-model="username" type="text" placeholder="Enter Username" name="username" required />

The x-model attribute, as you probably guessed, is similar to Vue.js’s v-model attribute and implements a two-way binding between the variable and the value of the element.

For one way binding, the x-bind attribute is used and similar to Vue.js there is the shorter syntax of :attr. The following two example are equivalent:

<a x-bind:href="homeUrl">Home</a>
<a :href="homeUrl">Home</a>

Two other one-way bindings similar to the x-bind attribute are the x-text and the x-html attributes. The first one will update the element’s innerText and the second the element’s innerHTML values. At our login form example, we used the x-text attribute to display a welcome login message based on user’s username:

7<div x-show="login" x-text="`welcome ${username}`"></div>

Toggle Display

The x-show attribute in Alpine.js toggles the display:none element’s style depending on the expression’s outcome. The above example will show the welcome message when the login is set to be true.

Another similar attribute is the x-if, which completely removes the element from the DOM but has two significant constraints. Because the Alpine uses the real DOM and not a virtual, the first constraint is that the x-if attribute must be applied on a <template></template> tag. Consequently, the second constraint is that the <template></template> must have a single element root. The equivalent of the above x-show example using the x-if attribute is:

<template x-if="login">
    <div x-text="`welcome ${username}`"></div>


The same limitations are applied for the x-for attribute, which creates new DOM nodes based on an array similar to the Vue’s v-for.:

<template x-for="item in items" :key="item">
    <div x-text="item"></div>

For inner loops the same considerations is applied:

1<template x-for="item in items">
2    <div>
3        <template x-for="subItem in item.subItems">
4            <div x-text="subItem"></div>
5        </template>
6    </div>

The limitation which the template tag enforces must be under constant consideration when you want to use loops or the x-if attribute.


For listening and responding to events, the x-on:event or the alternative syntax @:event is used. Similar to Vue, the x-on attaches an event listener to the corresponding element’s event. When that event is emitted, the specified expression is executed. In our example, when the form is submitted, we check if the password is correct and then setting the corresponding variable.

2 <form x-show="!login" x-on:submit.prevent="error=false;
3         if (password === 'pass') 
4            login = true; 
5         else error = true;" method="post">

The final result looks like this:


The Alpine.js advantages present during simple DOM manipulation based on user interactions; therefore, it is most suitable for:

  • showing, hiding, or removing DOM nodes under certain conditions
  • two-way or one-way binding of attributes
  • watching and responding to user/UI events

In the next article in the Alpine.js series, I will write some more advantage tools like:

  • reusable functions for minimizing javascript code in DOM and allowing code reuse
  • the spruce library as a global state for simplifying the inter-component communication
  • several magic helpers, which will help facilitate some common patterns like ajax interactions or parent component access.

The above tools help us to use Alpine.js for more advanced implementations.

In the end, if you seek a suitable and easy replacement for jQuery, I think you will find Alpine.js most suitable.


comments powered by Disqus

Also Read:

One of the constant requests a freelancer programming has is constructing a landing page for a project/company/research project etc. Therefore, it is better to have an already starter template adopted as a base to iterate upon it quickly.
One of the most common web app patterns involves collecting data from a form and submitting it to a REST API or, the opposite, populating a form from data originating from a REST API. This pattern can easily be achieved in Alpinejs using the native javascript Fetch Api. As a bonus, I describe the fetch async version at the end of the article.
One of the most frequent requirements when writing AlpineJs components is the communication between them. There are various strategies for how to tackle this problem. This article describes the four most common patterns that help pass information between different Alpinejs components.