Getting started with Alpine.js

There are so many Javascript frameworks at the moment and really the ones that get the most attention are the big three Angular, React, and Vue.js. As a Laravel developer, there is a framework that you will come across as it’s part of a collection of technologies that make up the TALL stack, that is Alpine.js which touts itself as a light Javascript framework and alternative to jQuery.

alpinejs-site.png

Code examples

The Alpine.js website has a page that acts as a great introduction to Alpine.js where a few examples are provided on how to use some of the basic Alpine.js features. I will cover the same examples found on that page but in my own detail here.

getting-started.png

To get started all you really need is Visual Studio Code with the Live Server extension and Google Chrome.

Start by creating an HTML page with the following code that can be used as a template, and load the page using the live server extension.

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
    </head>
    <body>
        <h1>Alpine.js</h1>
    </body>
</html>

Now you can begin adding in some Alpine.js code with the following, which will define a string variable and then output it as text.

<div class="ms-2" x-data="{ message: 'I ❤️ Alpine' }" x-text="message"></div>

The x-data attribute is used to define your data and the x-text attribute will output the defined value as the text value for the HTML element.

<div x-data="{ count: 0 }">
    <button class="ms-2 btn btn-dark" @click="count++">Increment</button>
    <span x-text="count"></span>
</div>

This example shows interaction with an HTML button, clicking the button will hide and show the hidden DIV element.

<div x-data="{ open: false }">
    <button class="ms-2 btn btn-dark" @click="open =! open">Toggle</button>
    <div x-show="open" @click.outside="open = false">Contents...</div>
    <!--
    Modifiers
    https://alpinejs.dev/directives/on#modifiers
    -->
</div>

A boolean variable open is declared as false, as it’s false the x-show will hide the element as it's not true. The button with the click event will toggle the open variable every time it's clicked, so it will be set to true if false and set to false when true.

Every time the open is set to true the x-show will show the DIV element and the text will show on the page.

<div
    class="ms-2"
    x-data="{
        search: '',
        items: ['foo', 'bar', 'baz'],
        get filteredItems() {
            return this.items.filter(
                i => i.startsWith(this.search)
            )
        }
    }">
    <input x-model="search" placeholder="Search...">
    <ul>
        <template x-for="item in filteredItems" :key="item">
            <li x-text="item"></li>
        </template>
    </ul>
    <!--
    Binding values to inputs using models
    https://alpinejs.dev/directives/model
    -->
</div>

The last example will define two variables in the x-data variable which are search and items. Search will be bonded to the input field so whatever is typed in the text field will be set to the search field. The items are a fixed list of items as defined, the get statement will bind the filteredItems function to the data so referencing filteredItems will call the function.

As the text is typed in the field then the filteredItems function will filter the items shown and match the text with the items in the items variable.

Completed HTML page with all examples

After going through all the examples I put them all together in a single HTML file and included some Bootstrap CSS in there so it's easy to distinguish between the different examples.

examples.png

The full code for the page is available here, use it as a refresher for when you are starting to play with Alpine.js and add to it once you discover and learn the other attributes that the framework provides.

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
        <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    </head>
    <body>
        <h1>Alpine.js Examples</h1>
        <!-- Example 1 -->
        <h2 class="mt-4">Set data and output data</h2>
        <div class="ms-2" x-data="{ message: 'I ❤️ Alpine' }" x-text="message"></div>
        <!-- Example 2 -->
        <h2 class="mt-4">Click action</h2>
        <div x-data="{ count: 0 }">
            <button class="ms-2 btn btn-dark" @click="count++">Increment</button>
            <span x-text="count"></span>
        </div>
        <!-- Example 3 -->
        <h2 class="mt-4">Click action</h2>
        <div x-data="{ open: false }">
            <button class="ms-2 btn btn-dark" @click="open =! open">Toggle</button>
            <div x-show="open" @click.outside="open = false">Contents...</div>
            <!--
            Modifiers
            https://alpinejs.dev/directives/on#modifiers
            -->
        </div>
        <!-- Example 4 -->
        <h2 class="mt-4">Filter action</h2>
        <div
            class="ms-2"
            x-data="{
                search: '',
                items: ['foo', 'bar', 'baz'],
                get filteredItems() {
                    return this.items.filter(
                        i => i.startsWith(this.search)
                    )
                }
            }">
            <input x-model="search" placeholder="Search...">
            <ul>
                <template x-for="item in filteredItems" :key="item">
                    <li x-text="item"></li>
                </template>
            </ul>
            <!--
            Binding values to inputs using models
            https://alpinejs.dev/directives/model
            -->
        </div>
    </body>
</html>