pinia
Pinia state management for Vue. Covers stores, actions, and getters. Use for Vue 3 state management. USE WHEN: user mentions "pinia", "vue state", "vue store", asks about "defineStore", "vue 3 state management", "composition stores", "vue global state", "storeToRefs", "pinia plugin" DO NOT USE FOR: React apps - use `zustand` or `redux-toolkit`; server data - use composables with fetch/axios; Vuex projects - migrate to Pinia first
What this skill does
# Pinia Core Knowledge
## Store Definition
```typescript
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Eduardo',
}),
getters: {
doubleCount: (state) => state.count * 2,
doublePlusOne(): number {
return this.doubleCount + 1;
},
},
actions: {
increment() {
this.count++;
},
async fetchData() {
const data = await api.getData();
this.count = data.count;
},
},
});
```
> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `pinia` for comprehensive documentation.
## Setup Syntax (Composition API)
```typescript
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const name = ref('Eduardo');
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
async function fetchData() {
const data = await api.getData();
count.value = data.count;
}
return { count, name, doubleCount, increment, fetchData };
});
```
## Usage in Components
```vue
<script setup>
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const store = useCounterStore();
// Reactive destructure
const { count, doubleCount } = storeToRefs(store);
// Actions can be destructured directly
const { increment } = store;
</script>
<template>
<button @click="increment">{{ count }}</button>
<p>Double: {{ doubleCount }}</p>
</template>
```
## Persist Plugin
```typescript
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
// In store
export const useUserStore = defineStore('user', {
state: () => ({ token: '' }),
persist: true, // or { storage: sessionStorage }
});
```
## When NOT to Use This Skill
| Scenario | Use Instead |
|----------|-------------|
| React applications | `zustand` or `redux-toolkit` |
| Server state (API data, caching) | Vue composables with `useFetch` or `useAsyncData` |
| Component-local state | Vue's `ref`/`reactive` |
| Vuex legacy projects | Migrate to Pinia first, or keep Vuex for now |
| Simple key-value storage | `localStorage` or `sessionStorage` directly |
## Anti-Patterns
| Anti-Pattern | Why It's Bad | Correct Approach |
|--------------|--------------|------------------|
| Using Options API stores | Less flexible than Composition API | Use `defineStore` with setup function |
| Not using `storeToRefs` | Loses reactivity on destructuring | Wrap with `storeToRefs(store)` |
| Storing server data in Pinia | No cache invalidation | Use composables with fetch/axios |
| Persisting everything | Large storage, security risks | Only persist necessary state |
| Mutating state from components | Breaks single source of truth | Always use actions |
| Circular dependencies between stores | Hard to debug, initialization issues | Use getters or separate composables |
| Not resetting stores on logout | Data leaks between users | Call `$reset()` or reset manually |
| Using global Pinia instance everywhere | Hard to test | Pass pinia instance explicitly in tests |
| No TypeScript types | Loses type safety | Define interfaces for state/getters/actions |
| Accessing stores outside setup | Can cause reactivity issues | Only use stores in setup or composables |
## Quick Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Lost reactivity after destructuring | Not using `storeToRefs` | Use `const { count } = storeToRefs(store)` |
| "getActivePinia was called with no active Pinia" | Store used before app mount or outside Vue | Ensure `app.use(pinia)` before accessing stores |
| Persist not working | Plugin not installed | Add `pinia.use(piniaPluginPersistedstate)` |
| State not resetting with `$reset()` | Using setup syntax without reset logic | Manually implement reset or use Options API |
| TypeScript errors with getters | Wrong return type inference | Explicitly type getter return value |
| Actions not updating components | State not reactive | Use `ref()` or `reactive()` in setup stores |
| Hot reload breaks stores | HMR issues with Vite | Add `if (import.meta.hot) { acceptHMRUpdate(...) }` |
| Can't access router in store | Router not injected | Inject router via plugin or pass as argument |
## Production Readiness
### Store Organization
```typescript
// stores/index.ts - Centralized store setup
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import { markRaw } from 'vue';
import router from '@/router';
export const pinia = createPinia();
// Add plugins
pinia.use(piniaPluginPersistedstate);
// Add router to all stores
pinia.use(({ store }) => {
store.router = markRaw(router);
});
// stores/authStore.ts - Production-ready auth store
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null);
const token = ref<string | null>(null);
const isAuthenticated = computed(() => !!token.value);
async function login(credentials: LoginCredentials) {
try {
const response = await api.login(credentials);
token.value = response.token;
user.value = response.user;
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}
function logout() {
token.value = null;
user.value = null;
// Clear all other stores
const userStore = useUserStore();
userStore.$reset();
}
return { user, token, isAuthenticated, login, logout };
}, {
persist: {
key: 'auth',
storage: localStorage,
paths: ['token'], // Only persist token
},
});
```
### Security Best Practices
```typescript
// Secure persistence with encryption
import CryptoJS from 'crypto-js';
import type { StorageLike } from 'pinia-plugin-persistedstate';
const SECRET = import.meta.env.VITE_STORE_SECRET;
const encryptedStorage: StorageLike = {
getItem(key: string): string | null {
const encrypted = localStorage.getItem(key);
if (!encrypted) return null;
try {
const bytes = CryptoJS.AES.decrypt(encrypted, SECRET);
return bytes.toString(CryptoJS.enc.Utf8);
} catch {
return null;
}
},
setItem(key: string, value: string): void {
const encrypted = CryptoJS.AES.encrypt(value, SECRET).toString();
localStorage.setItem(key, encrypted);
},
};
export const useSecureStore = defineStore('secure', {
state: () => ({ sensitiveData: null }),
persist: {
storage: encryptedStorage,
},
});
```
### Testing Stores
```typescript
// tests/stores/authStore.test.ts
import { setActivePinia, createPinia } from 'pinia';
import { useAuthStore } from '@/stores/authStore';
import { vi } from 'vitest';
describe('AuthStore', () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it('should login successfully', async () => {
const store = useAuthStore();
vi.spyOn(api, 'login').mockResolvedValue({
token: 'jwt-token',
user: { id: '1', name: 'John' },
});
const result = await store.login({ email: '[email protected]', password: 'password' });
expect(result.success).toBe(true);
expect(store.isAuthenticated).toBe(true);
expect(store.user?.name).toBe('John');
});
it('should clear state on logout', () => {
const store = useAuthStore();
store.token = 'token';
store.user = { id: '1', name: 'John' };
store.logout();
expect(store.token).toBeNull();
expect(store.user).toBeNull();
expect(store.isAuthenticated).toBe(false);
});
});
```
### Error Handling
```typescript
// stores/errorStore.ts
export const useErrorStore = defineStore('error', () => {
const errors = ref<AppError[]>([]);
function addError(error: AppError) {
errors.value.push({
...error,
id: crypto.randomUUID(),
timestamp: Date.now(),
});
// Auto-remove after 5 sRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.