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 name you'll use to access the store (e.g., 'user', 'notifications').
- value (object): An object containing the store's data (properties) and methods to manipulate that data. Properties within this object are reactive; when they change, any part of your UI bound to them will automatically update.
// Example store definition
Alpine.store('theme', {
darkMode: false,
toggle() {
this.darkMode = !this.darkMode;
}
});
$store.storeName.property: Within your Alpine components' HTML templates, you can access a store's properties using this syntax.
For example, to access the darkMode property from the theme store defined above: <div :class="$store.theme.darkMode ? 'dark-mode' : ''">...</div>.
Methods within store to modify state: It's best practice to define methods within your store object to handle state modifications. This encapsulates the logic for state changes and keeps your components cleaner.
For example, calling the toggle method: <button @click="$store.theme.toggle()">Toggle Theme</button>. When toggle() modifies this.darkMode, Alpine's reactivity system ensures all dependent UI elements update.
alpine:init event: Global stores (and global component data via Alpine.data()) should be defined *inside* an event listener for alpine:init. This event fires once Alpine.js is fully initialized and ready. Defining stores here ensures they are available before any components on the page try to access them.
document.addEventListener('alpine:init', () => {
Alpine.store('myStore', { /* ... */ });
// Alpine.data(...) can also go here
});
Initialization Order: Stores must be defined before components attempting to access them are initialized. The most reliable way to ensure this is by defining all your stores within a document.addEventListener('alpine:init', () => { ... }); block. If a component tries to access $store.someStore before Alpine.store('someStore', ...) has been called, you'll encounter errors (typically $store.someStore will be undefined).
Store Complexity and Organization: For simple applications, a single global store might suffice. However, as your frontend grows, a monolithic store can become difficult to manage and debug. Consider breaking down your global state into multiple, focused stores (e.g., Alpine.store('user', ...), Alpine.store('cart', ...), Alpine.store('notifications', ...)). This improves modularity and makes it easier to understand where specific pieces of state are managed. Think of it like organizing your Python backend into different modules or apps.
Reactivity with Objects/Arrays: When mutating objects or arrays within a store (e.g., adding an item to an array), ensure you're doing it in a way that Alpine's reactivity system can detect. For arrays, methods like push(), pop(), splice() are fine. If you replace an entire array or object, that's also fine (e.g., this.items = [...this.items, newItem] or this.items.push(newItem)). Directly setting an index (e.g. this.items[0] = newValue) for an array or adding a new property to an object after initialization might not trigger reactivity unless using specific techniques or reassigning the object/array (less common with Alpine stores compared to Vue/React).