Description: Alpine.store allows you to define and access shared, reactive data or state that is accessible from any Alpine component on the page. This is particularly useful for managing global concerns like user authentication status, theme preferences, or, as shown in the example, a global loading state for API calls.
Alpine.store('storeName', { dataProperty: 'value', method() {} }) (to define a store)
This function registers a global store with AlpineJS.
'storeName': A unique string identifier for your store (e.g., 'user', 'cart', 'appStatus').dataProperty: 'value': These are your state variables. They are reactive, meaning Alpine will automatically update the UI whenever they change. For example, isLoading: false, currentUser: null.method() {}: Functions defined within the store object can be used to manipulate the store's data or perform other logic. Inside these methods, this refers to the store object itself, allowing you to access and modify its properties (e.g., this.isLoading = true).// Example store definition
Alpine.store('userSession', {
isLoggedIn: false,
username: null,
login(name) {
this.isLoggedIn = true;
this.username = name;
},
logout() {
this.isLoggedIn = false;
this.username = null;
}
});
$store.storeName.dataProperty (to access store data in components)
Once a store is defined, you can access its data properties from any Alpine component's template (e.g., within x-text, x-show, x-bind, event handlers, etc.) using the magic $store object. For example, to display the username from the store defined above: <span x-text="$store.userSession.username"></span>.
$store.storeName.method() (to call store methods)
Similarly, you can call methods defined in your store from any Alpine component. For instance, to trigger the login method: <button @click="$store.userSession.login('Alice')">Log In</button>.
Defining a store after components try to access it: Alpine.store() must be called to define a store before any Alpine components that rely on it are initialized. The best practice is to define all your stores within a document.addEventListener('alpine:init', () => { ... }); callback. This ensures Alpine is ready and your stores are registered before component initialization begins. If defined in a separate script tag, that script must execute before Alpine's initialization process for components that use the store.
Modifying store data directly from many places without clear patterns: While you can directly modify store properties from any component (e.g., $store.cart.items.push(...) or $store.appStatus.isLoading = true), it's often better practice for complex applications to define methods within the store itself to encapsulate state mutations. This makes changes more predictable, centralized, and easier to debug. Think of these methods as "setters" or "service methods" in backend architectures. For example, instead of $store.cart.count++, define a method $store.cart.increment() that handles the logic (increment() { this.count++; }).
Overusing global state for data that is truly local to a component: Alpine.store is for data that genuinely needs to be shared across different, potentially unrelated, parts of your application. If data is only relevant to a single component or a small group of closely related components (e.g., a parent and its direct children), it's often cleaner and more maintainable to manage it using local component state with x-data, or through props and events for parent-child communication. Overusing global state can make it harder to track where data changes are coming from, similar to the overuse of global variables in Python.
This example demonstrates a global store (appState) used to manage a loading indicator and fetched data. Notice how two separate "components" (divs) on the page can react to and interact with the same global state.
API Status:
Current Global Loading State: LOADING... IDLE
Number of items in global store:
(This area reads directly from $store.appState)