Description: Manage application-wide state (e.g., user auth, theme) using centralized, reactive data stores accessible from any component via $store.
Alpine.store(name, value)
This is the core function used to define a global, reactive data store. It's typically called once per store within an alpine:init event listener.
name (String): A unique key to identify your store (e.g., 'userAuth', 'themeSettings').value (Object): An object containing:
isAuthenticated: false, username: null).login(), logout()). These methods have access to the store's data via this.Example definition:
// Inside document.addEventListener('alpine:init', () => { ... });
Alpine.store('cart', {
items: [],
total: 0,
addItem(item) {
this.items.push(item);
this.total += item.price;
},
removeItem(itemId) {
// logic to remove item and update total
const itemIndex = this.items.findIndex(i => i.id === itemId);
if (itemIndex > -1) {
this.total -= this.items[itemIndex].price;
this.items.splice(itemIndex, 1);
}
}
});
$store.storeName.property
Once a store is defined (e.g., Alpine.store('userAuth', { ... })), any Alpine component on the page can access its data properties and methods using the global $store magic property.
To read a property: <span x-text="$store.userAuth.username"></span>
This access is reactive. If $store.userAuth.username changes, the <span> will automatically update.
methods within store to modify state
State modification logic should be encapsulated within methods defined in the store object. This keeps your state management centralized and predictable.
To call a store method: <button @click="$store.userAuth.login('Alice')">Login</button>
Inside a store method, this refers to the store object itself, allowing you to modify its properties (e.g., this.isAuthenticated = true;).
alpine:init
This is a crucial browser event that AlpineJS dispatches after it has fully initialized itself but *before* it initializes any components on the page (elements with x-data).
You must register your global stores using Alpine.store() (and also reusable components with Alpine.data()) inside an event listener for alpine:init. This ensures that the stores are available when components start looking for them.
document.addEventListener('alpine:init', () => {
Alpine.store('settings', { darkMode: false });
// ... other stores or Alpine.data calls
});
For Python developers, think of alpine:init as a specific point in the "runtime loading" of Alpine where you define global resources, similar to how you might set up global configurations or singleton instances early in your Python application's lifecycle.
Stores must be defined before components access them (use alpine:init):
If an Alpine component (defined with x-data) tries to access $store.someStore.someProperty before Alpine.store('someStore', ...) has been executed, you'll encounter JavaScript errors (typically "Cannot read property 'someProperty' of undefined").
The alpine:init event listener is the correct place to define all your stores. Alpine guarantees that code within this listener runs after Alpine itself is ready and before any components are processed.
Python Analogy: This is like trying to use a variable or import a module that hasn't been defined or loaded yet. alpine:init ensures your "global state modules" (Alpine stores) are loaded first.
// Correct way:
document.addEventListener('alpine:init', () => {
Alpine.store('user', { name: 'Guest' });
Alpine.data('myComponent', () => ({
// Now it's safe to access $store.user here if needed,
// or directly in the template.
userName: Alpine.store('user').name
}));
});
// Incorrect: (Alpine.store might run too late or not be found)
// Alpine.data('myComponent', () => ({ /* ...uses $store.user... */ }));
// Alpine.store('user', { name: 'Guest' }); // Defined too late or outside alpine:init
Avoid monolithic stores for complex applications:
While it's tempting to put all global state into one giant store, this can quickly become unmanageable as your application grows. A single store with too many unrelated properties and methods becomes hard to reason about, debug, and maintain.
Instead, break down your global state into multiple, smaller, focused stores, each responsible for a specific domain or feature.
For example, instead of one appStore, consider:
Alpine.store('userSession', { /* auth status, user profile */ })Alpine.store('cart', { /* items, total, checkout methods */ })Alpine.store('uiTheme', { /* dark mode, font size */ })Python Analogy: This is similar to organizing a large Python project into multiple modules or packages instead of putting all your code into one massive .py file. It improves modularity, readability, and separation of concerns.
Status: Not Logged In
Status: Logged In
Welcome, !
Email:
Role:
This section also reflects the global authentication state: User "" is authenticated. No user is currently authenticated.