pinia-action-loader
v1.0.2
Published
Pinia plugin that automatically tracks the loading state of every store action
Maintainers
Readme
pinia-action-loader
A Pinia plugin that automatically tracks the loading state of every store action, so you never have to manage isLoading booleans by hand.
<btn :loading="isLoading(store.fetchUser)">Save</btn>
<!-- or if you prefer string reference namespaced: -->
<btn :loading="isLoading('user/fetchUser')">Save</btn>The argument is a direct function reference to the store action — so renaming or refactoring in your IDE just works.
Features
- Zero boilerplate — loading state is tracked automatically for every action in every store
- Query by action reference, action name string, or "is anything loading at all?"
- Filter concurrent calls by their arguments
- Works with Vue 2 and Vue 3
Installation
npm install pinia-action-loaderRegister the plugin when you create your Pinia instance:
import { createPinia } from 'pinia'
import { createLoaderPlugin } from 'pinia-action-loader'
const pinia = createPinia()
pinia.use(createLoaderPlugin)Usage
Basic — query by function reference
<script setup>
import { useUserStore } from './stores/user' // your own custom store
import { isLoading } from 'pinia-action-loader'
const store = useUserStore()
</script>
<template>
<btn :loading="isLoading(store.fetchUser)" @click="store.fetchUser(42)">
Load User
</btn>
</template>Passing the action as a function reference (store.fetchUser) gives you refactor-safe queries: if you rename the action, your IDE will update the reference automatically.
Query by action name string
If you prefer, you can query by the fully-qualified action name, which takes the form storeId/actionName:
isLoading('user/fetchUser')Is anything loading at all?
Calling isLoading() with no arguments returns true if any action across any store is currently in flight:
<global-spinner v-if="isLoading()" />Filtering concurrent calls by arguments
When the same action is called multiple times in parallel (e.g. fetching several users at once), you can filter by arguments using a predicate function:
// Is user 42 specifically being loaded?
isLoading(store.fetchUser, (userId) => userId === 42)The predicate receives the same arguments that were passed to the action.
Using the loader store directly
The underlying Pinia store is available if you need reactive access in more complex scenarios:
import { useLoaderStore } from 'pinia-action-loader'
const loaderStore = useLoaderStore()
// Reactive getter — works in computed properties and templates
loaderStore.isLoading(store.fetchUser)How it works
The plugin registers a $onAction listener on every Pinia store (except the loader store itself). When an action is called, it:
- Creates a loading entry keyed by a unique
Symbol, scoped under the action's fully-qualified name (storeId/actionName). - Removes that entry when the action resolves — whether it succeeds (
after) or throws (onError).
Because the same action can be in flight multiple times concurrently, each call gets its own Symbol-keyed slot inside the action's "bucket". The bucket is removed entirely once all concurrent calls have settled.
Action function references are mapped to their string names the first time each action is called. Pinia reuses the same function references across store instances, so this mapping stays valid for the lifetime of the app.
API
createLoaderPlugin
The Pinia plugin factory. Pass it directly to pinia.use().
pinia.use(createLoaderPlugin)isLoading(storeActionName?, filterFn?)
Standalone helper that queries the loader store.
| Argument | Type | Description |
|---|-----------------------------------|-------------------------------------------------------------------------------------------|
| storeActionName | function \| string \| undefined | Action function reference,'storeId/actionName' string,or omit to check globally |
| filterFn | (...args) => boolean | Optional predicate to match a specific concurrent call by its arguments |
Returns boolean.
useLoaderStore()
Returns the raw Pinia loader store. Useful for reactive access or when you need to call load/unload loader store actions manually.
Vue 2 support
Vue 2.7 is supported via vue-demi, which is already a transitive dependency of Pinia. No additional installation is required. Vue 2 support is expected to be dropped in a future major version, in line with Vue 2's end-of-life status.
License
MIT
