Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Global state: Alpine.store

Data from x-data belongs to one component. When the same data is needed in different parts of the page — a task counter in the header and the list itself in the middle — it goes into a store. Alpine.store() creates a global data object, and any component reads and changes it through $store.

Creating a store

A store, like a component, is registered in an alpine:init handler:

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<script>
  document.addEventListener('alpine:init', () => {
    Alpine.store('todos', {
      items: ['Buy milk']
    });
  });
</script>
 
<header x-data>
  Tasks: <span x-text="$store.todos.items.length"></span>
</header>
 
<main x-data>
  <button @click="$store.todos.items.push('New task')">Add</button>
</main>

The header and the button are two separate components. A click in one changes the number in the other: both work with the same todos store.

Methods and getters

A store can hold logic too. Inside its methods this points to the store itself:

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<script>
  document.addEventListener('alpine:init', () => {
    Alpine.store('todos', {
      items: [
        { id: 1, title: 'Buy milk', done: true },
        { id: 2, title: 'Call Oleg', done: false }
      ],
      get left() {
        return this.items.filter(task => !task.done).length;
      },
      toggle(task) {
        task.done = !task.done;
      }
    });
  });
</script>
 
<p x-data x-text="'Left: ' + $store.todos.left"></p>
 
<ul x-data>
  <template x-for="task in $store.todos.items" :key="task.id">
    <li @click="$store.todos.toggle(task)" x-text="(task.done ? '✓ ' : '') + task.title"></li>
  </template>
</ul>

At the top you see "Left: 1". Clicking a task toggles its mark, and the number is recalculated.

The init() method

If a store has an init() method, Alpine calls it on registration. It is a good place to prepare initial data:

// index.html, inside the alpine:init handler
Alpine.store('todos', {
  items: [],
  init() {
    this.items = [{ id: 1, title: 'Buy milk', done: false }];
  }
});

A store in an npm project

In src/main.js the store is registered before Alpine.start():

// src/main.js
import Alpine from 'alpinejs';
 
Alpine.store('todos', {
  items: []
});
 
Alpine.start();

From plain JavaScript outside the markup, the store is read the same way: Alpine.store('todos').items.

Component or store

DataWhere to keep it
Needed by one block: is the menu open, the field textx-data
Needed by several blocks: tasks, the user, the themeAlpine.store

Common mistake

Using $store on an element without x-data. Like every directive, x-text="$store.todos.left" works only inside a component. Give a standalone element that only needs store data an empty x-data, like the <header> in the first example.

Thanks for reading!

Donate to the site — we work hard for you.