AlpineJS. Not another JavaScript Framework

A minimal JS framework. Created by Caleb Porzio, which borrows the syntax from Vue, making it easy to pick up.

So why is it different?

It’s tiny in size, just over 4Kb at the time of writing. And it works with your current frontend templating. Whether that’s HTML, Blade or any other templating engine. It makes it easy to interact and manipulate the DOM.

In the author’s words, “it’s like tailwind for JS”.

It’s not meant to replace Vue, React or any of the other big boys. It doesn’t use a virtual DOM. No routing. No state management. And doesn’t have a built step. No webpack or rollup config.

Simply include it from the CDN and you’re good to go.

When would I use it?

I don’t think it’s meant to handle complex logic. It helps you with adding interactivity to elements, like toggling a drawer menu. Opening and closing a modal. And much more.

Enough said let’s dig in.

A simple todo example

We’re going to create a simple todo app, it will help you understand the concepts behind AplineJS.

You can jump straight to the code or walk through this example.

Create index.html

First let’s create an index.html file. And include Alpine in the <head> section.

Create an app.js file and include it just before the closing </body> tag.

  <script src="" defer></script>
  <script src="app.js" defer></script>

In our app.js let’s create a function holding the data for the todos, and the methods needed to add and remove a todo.

  function todos() {
    return {
      todos: [],
      todo: {
        id: 1,
        name: ""
      addNewTodo() {
        if ( === "") {
        this.todos.push({ id:, name:, done: false });; = "";
      deleteTodo(todoToRemove) {
        this.todos = this.todos.filter(todo => {
          return !==;

The function returns an object. In it we have an array holding our todos, a method to add a todo.

And a method deleteTodo() which uses the filter() method to remove a todo from the list.

Simple, no?

Now let’s declare a component in our index.html. We do this by using the x-data directive. We’ll bind it to the todos() function.

While we’re at it we’ll also create a form to add new todos.

<div id="app" x-data="todos()">
  <form @submit.prevent="addNewTodo()">
    <label for="todo" >Add new todo</label>
    <input type="text" name="todo" x-model="" />

If you’re coming from Vue, two things should be familiar.

@submit.prevent="... an event listener that works just like it does in Vue.

And x-model="... which adds 2-way data binding. Similar to the v-model in Vue.

Now let’s loop over our todos and display them.

Alpine provides us with x-for="... you’ve guessed it works just like v-for="... in Vue. However in Alpine we cannot use it on any HTML element. We have to use it on a <template>.

<template x-for="item in todos" :key="">
    <p x-text="" :class="{ 'line-through': item.done }"></p>
    <input type="checkbox" x-model="item.done" />
    <button @click="deleteTodo(item)">X</button>

I use x-text="... to bind the text.

:class="... to bind a class when the condition is true.

And finally the @click="... event listener to add new todos. And similarly I have a button with another @click event listener to remove a todo.

That’s it, our Todo is ready. It won’t win any awards but hopefully shows you how simple it is to work with Alpine, especially if you’re coming from Vue. Here’s a full working example.

My thoughts

I like how it works in harmony with whatever templating engine I’m using.

I had quite a few projects where I wished to use Vue for its simple syntax, but was an overkill for the project. Now I can use Alpine. It’s light, nimble and helps me write better code.

It doesn’t do anything that Vanilla JS doesn’t. But it makes my life easier.