Clocking in at 7.1kb gzipped, Alpine.js packs a heavy punch. Built to be a "drop and develop" JavaScript framework, it can add a lot of functionality to your application with little configuration. I have been working with Alpine.js for the last year and I have to say I really enjoy its syntax and overall ease of use. It does not rely on a development environment and its directives can be used directly in HTML. In this article, we will take a look at the methods Alpine.js provides as well as how you can create your own custom contexts to extend the functionality.

Spongebob with caption, look at all this cool stuff

Installation

Alpine.js can be instantiated via CDN or as a node module. For specific instructions on how to set up Alpine.js using a node module check out their great documentation. For the sake of this article, we will be using the CDN link to get it up and running. We place the CDN link in the head of the document so we can be sure it loads before our content.

<html>
  <head>
    ...
    <script
      defer
      src="https://unpkg.com/alpinejs@3.10.2/dist/cdn.min.js"
    ></script>
  </head>
  ...
</html>

Directives

There are several directives that Alpine.js provides and we won't cover all of them, but here are a few key ones that you will frequently utilize when developing an application.

x-data

The most important of all of them, x-data provides context for your component to reference, here you can define variables or even create a context to hold all of the functionality for that block of code. For example, if we want to declare the text that should be rendered within a component we can define the text in x-data and use another directive, x-text to print that data.

<div x-data="{ firstName: 'Hunter' }">
  <h1 x-text="firstName"></h1>
</div>

We initiate the value of firstName in our x-data and then use x-text to print that within our h1 element. While this is useful, I find that creating a context provides a lot more flexibility as values/methods defined in an x-data context can be inherited by the child elements. We need to first listen for the event alpine:init that is fired once Alpine has finished loading and then we can register a new context.

<div x-data="name">
  <h1 x-text="firstName"></h1>
  <button @click="handleClick">Send my name to the console</button>
</div>

<script>
  document.addEventListener("alpine:init", ()=>{

      Alpine.data("name", ()=>{

          firstName: "Hunter",

          handleClick() {
              console.log(this.firstName);
          }
      })
  })
</script>

By creating a custom x-data context we have created a reusable set of logic that can be utilized and inherited by any children where x-data="name" has been set since "name" is the namespace we used to register our context. We can also define custom methods to be called when using another directive x-on or @ for short to bind events to our element. In this case, we are binding a click event to the button and printing my name to the console when it is clicked. For a more in-depth look at what x-data can do, take a look at the documentation.

x-bind

When developing an application you often need to dynamically assign values to attributes in your HTML, that is where x-bind comes in to save the day. As with x-on using the @ as a shorthand, x-bind uses : to notate a bound attribute. In the below example we have set our x-data to handle the state of a menu, we have initialized the variable isOpen to store the state of the menu. When our button is clicked we toggle the isOpen state to the opposite (true or false). And our menu has a bound class that uses a ternary operator to determine what class to print. We can also simultaneously register an unbound attribute such as class to hold our base styles.

<div x-data="{ isOpen: false }">
  <button @click="isOpen = ! isOpen">Toggle Menu</button>
  <div :class="isOpen ? 'menu--active' : 'menu--closed'" class="menu">
    <!--menu content-->
  </div>
</div>

More information on x-bind can be found in the documentation.

x-model

Most frameworks have some sort of built-in method to handle binding the value of an attribute to a variable. With React you can use State or Refs, Vue has v-model, and Alpine.js has x-model. Having a way to dynamically bind the value of an element can save a ton of time when developing especially with large forms. All that needs to be done to use this is to add an x-model to an element that can return a value and assign the variable that is used to store the data. Whenever the value of the input is changed, the x-model will reflect the new value instantly. In our below example we are initiating our component with a firstName variable and binding it to our input using x-model. Pair this with a custom x-data context as we saw before and you can handle forms with ease.

<form x-data="{ firstName: '' }">
  <label for="firstName">First Name</label>
  <input id="firstName" name="firstName" type="text" x-model="firstName" />
</form>

x-for

When developing you often encounter repeatable content and while you can hardcode them or use JavaScript to dynamically render a list of items from an array, Alpine.js has a directive that handles this gracefully. With x-for you can loop over a template and dynamically render content from an array. In the below example we have a list of developers, each with an ID which we will use as a key to help Alpine.js keep track of the rendered elements. We define the array in our x-data and use a template to iterate over the elements we want to print on the page.

<ul
  x-data="{ developers: [
    { id: 1, name: 'Hunter' },
    { id: 2, name: 'Ceri' },
    { id: 3, name: 'Grace' },
]}"
>
  <template x-for="dev in developers" :key="dev.id">
    <li x-text="dev.name"></li>
  </template>
</ul>

We use x-bind to bind the value to our key attribute in the template and the content is looped over giving us the following result in HTML

<ul>
  <li>Hunter</li>
  <li>Ceri</li>
  <li>Grace</li>
</ul>

x-if and x-show

Conditional rendering is a very common need when building out a dynamic webpage, Alpine.js comes with a few directives that assist with this.

x-if

When you want to conditionally render certain content, x-if will dynamically render or prevent rendering based on the case it is provided. In the below example we have a variable isLoggedIn that determines if the user is authenticated or not. While authenticated content should be handled at a route level, something like a log-in button might need to be removed if the user has an authenticated status. Since our content is wrapped in a <template> tag it will not be rendered unless Alpine forces it to do so.

<div x-data="{isLoggedIn: false}">
  <template x-if="isLoggedIn">
    <!--Authenticated Content-->
  </template>
</div>

x-show

Similar to x-if, x-show conditionally hides an element based on the specified parameter. We used a menu example before when looking at x-bind, instead of dynamically adding a class here, we can use x-show to visually hide the element based on the value of isOpen.

<div x-data="{ isOpen: false }">
  <button @click="isOpen = ! isOpen">Toggle Menu</button>
  <div x-show="isOpen" class="menu">
    <!--menu content-->
  </div>
</div>

Conclusion

Alpine.js is a brilliant framework that can handle a lot of heavy lifting in your application without needing to load in a large library or be bound to a specific development environment. I implore you to read further into their documentation as it is top-notch and covers a lot more capabilities than I highlighted in this article.

Happy Coding!