Working with arrays in Alpine.js stores

Bjorn Krols

Bjorn Krols

Published on
25 May 2022

In this article, you will learn how to work with arrays in Alpine.js stores.

Alpine.js is a rugged, minimal framework for composing JavaScript behavior in your markup.

The Alpine.store() API offers global state management.

This article is compatible with Alpine.js V3.

You will be building a tiny application that lets you create, view and updates messages.

Start with the following minimal HTML.

<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <form>
      <div>
        <label>Message:</label>
        <textarea name="message" required></textarea>
      </div>
      <button>Submit</button>
    </form>

    <div>
      <hr />
      <div>My first message</div>
      <button>lowercase</button>
      <button>UPPERCASE</button>
      <button>Delete</button>
      <hr />
      <div>My second message</div>
      <button>lowercase</button>
      <button>UPPERCASE</button>
      <button>Delete</button>
    </div>
  </body>
</html>

Let's enhance it with some Alpine.js magic. Add the following script tag inside head.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="//unpkg.com/alpinejs" defer></script>
  </head>
  <body>
    <!-- ... -->
  </body>
</html>
  • The defer attribute makes the script execute after the page has finished parsing.
  • The two forward slashes are a common shorthand for "whatever protocol is being used right now".

x-data marks a chunk of HTML that should be controlled by Alpine.js.

Create an empty store (I have called it main, but you can pick what you prefer).

<body x-data>
  <!-- ... -->
  <script>
    document.addEventListener("alpine:init", () => {
      Alpine.store("main", {});
    });
  </script>
</body>

Add the required state and helper functions to your store.

Alpine.store("main", {
  // The array of all messages
  messages: [],

  // The next message to add, its value is bound to the textarea field
  newMessage: "",

  // Adds the current value of `newMessage` to the array of messages
  addMessage(message) {
    this.messages.push(message);
    this.newMessage = "";
  },

  // Given an index, changes the capitalization of the message to lower case
  lowerCaseMessage(index) {
    this.messages[index] = this.messages[index].toLowerCase();
  },

  // Given an index, changes the capitalization of the message to upper case
  upperCaseMessage(index) {
    this.messages[index] = this.messages[index].toUpperCase();
  },

  // Given an index, removes the message from the array
  deleteMessage(index) {
    this.messages = this.messages.filter((_, i) => i !== index);
  },
});

Let's implement the form.

When working with a store's properties and functions, you need to prefix all calls with $store.nameOfYourStore.

To set up input reactivity, use x-model to bind newMessage to the textarea.

The form element emits a submit event you can listen to using the @submit directive.

Add the .prevent modifier to prevent the browser from submitting a native form request.

<!-- ... -->
<form @submit.prevent="$store.main.addMessage($store.main.newMessage)">
  <div>
    <label>Message:</label>
    <textarea
      name="message"
      required
      x-model="$store.main.newMessage"
    ></textarea>
  </div>
  <button>Submit</button>
</form>
<!-- ... -->

Let's implement the list.

Use x-for to loop over the array.

<!-- ... -->
<div>
  <template x-for="message, index in $store.main.messages">
    <div>
      <hr />
      <div x-text="message"></div>
      <button @click="$store.main.lowerCaseMessage(index)">lowercase</button>
      <button @click="$store.main.upperCaseMessage(index)">UPPERCASE</button>
      <button @click="$store.main.deleteMessage(index)">Delete</button>
    </div>
  </template>
</div>
<!-- ... -->

You can access a store's state and call its functions externally.

<!DOCTYPE html>
<html lang="en">
  <body x-data>
    <form @submit.prevent="$store.main.addMessage($store.main.newMessage)">
      <!-- ... -->
    </form>

    <div>
      <!-- ... -->
    </div>

    <script>
      // ...
    </script>

    <script>
      let i = 0;
      setInterval(() => {
        Alpine.store("main").addMessage(`Hello, Interval! (${i})`);
        i++;
      }, 3000);
    </script>
  </body>
</html>

Thank you for reading through, feel free to leave a comment below.

Subscribe to our newsletter

The latest news, articles, and resources, sent to your inbox weekly.

More like this