Description: Manage application-wide state (e.g., user authentication status, theme preferences, global notifications) using centralized, reactive data stores. These stores are accessible from any Alpine.js component on the page via the magic $store object.
Alpine.store(name, value):
This is the primary function used to define a global store.
name (string): The unique identifier for your store (e.g., 'user', 'cart', 'notifications').value (object): An object containing the store's data (properties) and methods to manipulate that data. Properties within this object will be reactive.Example of defining a simple theme store:
// Inside alpine:init event listener
Alpine.store('theme', {
darkMode: false,
toggle() {
this.darkMode = !this.darkMode;
}
});
$store.storeName.property:
Once a store is defined, you can access its properties from any Alpine component's HTML template or JavaScript logic using the $store magic property.
For the 'theme' store example above:
<div :class="$store.theme.darkMode ? 'dark-mode-class' : 'light-mode-class'">...</div><button @click="$store.theme.toggle()">Toggle Theme</button>Methods within store to modify state:
It's best practice to modify store data through methods defined within the store itself. This encapsulates the logic for state changes and makes the store more maintainable. Alpine.js ensures that when these methods modify store properties, any components using those properties will reactively update.
// Inside alpine:init event listener
Alpine.store('counter', {
count: 0,
increment() {
this.count++;
},
decrement() {
if (this.count > 0) this.count--;
}
});
Usage: <span x-text="$store.counter.count"></span> and <button @click="$store.counter.increment()">+</button>.
alpine:init event:
This JavaScript event is dispatched on the document object once Alpine.js has finished its initial setup but before it initializes components on the page. It is the recommended place to define your global stores using Alpine.store() and register global components with Alpine.data(). This ensures stores are available when components try to access them.
document.addEventListener('alpine:init', () => {
Alpine.store('myStore', { message: 'Hello from store!' });
Alpine.data('myComponent', () => ({
// component logic
}));
});
Store Definition Timing: Stores must be defined before any component attempts to access them. The safest way to ensure this is to define all your Alpine.store() calls within a document.addEventListener('alpine:init', () => { ... }) block. If a component initializes and tries to access $store.someStore before Alpine.store('someStore', ...) has been executed, it will result in errors (typically $store.someStore being undefined).
Store Organization for Complex Apps: For applications with significant global state, avoid creating one gigantic, monolithic store. This can become difficult to manage and debug. Instead, consider:
Alpine.store('app', { user: {...}, settings: {...} }).Alpine.store('userAuth', {...}), Alpine.store('cart', {...}), Alpine.store('uiMessages', {...}). This improves modularity and makes it easier to understand where specific pieces of state are managed. Python developers familiar with modular design or microservices might find this analogous to breaking down a large application into smaller, manageable services or modules.Reactivity and Objects/Arrays: When modifying objects or arrays within a store, ensure you are doing so in a way that Alpine's reactivity system can detect. For arrays, methods like push, pop, splice, etc., are fine. For objects, reassigning the property or modifying it directly works (e.g., this.user.name = 'New Name' or this.user = { ...newUserObject }). Avoid directly reassigning the entire store object from outside its methods if you expect reactivity to propagate correctly for deeply nested changes without careful consideration. Modifying through store methods is always the safest bet.