vue pinia
via PatrickJS/awesome-cursorrules
Vue 3 and TypeScript state management with Pinia stores, Composition API, and SSR patterns.
What is vue pinia?
Guidance for building scalable Vue 3 applications with Pinia, covering store structure, component integration, TypeScript practices, and SSR considerations. Use this rule when managing shared client state across routes, layouts, or component trees in Vue 3 projects.
- Structure stores using setup syntax with explicit state, computed getters, and actions organized by domain
- Use storeToRefs() for safe destructuring and maintain reactivity in components
- Type store state, payloads, and API responses explicitly; avoid any and use discriminated unions for workflows
- Handle async workflows with explicit status, error, and lastUpdatedAt fields for UI-dependent operations
- Persist only non-sensitive fields with validation and migrations; never store secrets or tokens
- Test stores directly and reset Pinia between tests to prevent state leakage
Applies to
File patterns this rule matches.
Rule definition (reference)
Source of truth, from the repository.
You are an expert in Vue 3, TypeScript, and Pinia state management.
Vue + Pinia Guidelines
State Ownership
- Keep component-only state in the component with
ref,reactive, orcomputed. - Use Pinia for shared client state that spans routes, layouts, or unrelated component trees.
- Use route params and query strings for shareable navigation state.
- Use Nuxt
useFetch/useAsyncData, TanStack Query for Vue, or the existing API layer for server state. - Do not copy server cache data into Pinia unless it is an intentional editable draft, offline cache, or workflow snapshot.
Store Structure
- Prefer setup stores with
defineStore('name', () => { ... })for Vue 3 Composition API projects. - Use one store file per domain under
stores/, such asstores/cart.tsorstores/session.ts. - Keep state, computed getters, and actions together when they represent one cohesive domain.
- Return every state property from setup stores so Pinia can track it for devtools, SSR, and plugins.
- Keep getters pure and side-effect free; put writes, I/O, and orchestration in actions.
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
interface CartLine {
id: string
name: string
quantity: number
unitPrice: number
}
export const useCartStore = defineStore('cart', () => {
const lines = ref<CartLine[]>([])
const itemCount = computed(() =>
lines.value.reduce((total, line) => total + line.quantity, 0),
)
function addLine(line: CartLine) {
const existing = lines.value.find((item) => item.id === line.id)
if (existing) {
existing.quantity += line.quantity
return
}
lines.value.push(line)
}
function clearCart() {
lines.value = []
}
return {
lines,
itemCount,
addLine,
clearCart,
}
})
Component Usage
- Call stores at the top of
<script setup>or inside setup functions, getters, and actions. - Use
storeToRefs()when destructuring store state or getters in components. - Destructure actions directly when useful; actions remain bound to the store.
- Avoid writing large business workflows in components; move them to store actions or composables.
- Prefer computed values over watchers when deriving state.
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
const { itemCount } = storeToRefs(cart)
const { clearCart } = cart
</script>
<template>
<button type="button" :disabled="itemCount === 0" @click="clearCart">
Clear cart
</button>
</template>
TypeScript
- Type store state, action payloads, and API responses explicitly.
- Avoid
any; useunknownand narrow external inputs before committing them to state. - Use interfaces for object state that is shared across components or API boundaries.
- Prefer discriminated unions for workflow status and error state.
- Keep store IDs stable and descriptive because they appear in devtools and persistence keys.
Actions and Side Effects
- Actions may be sync or async; keep each action focused on one user or domain workflow.
- Validate action inputs at the boundary before mutating store state.
- Represent async workflows with explicit
status,error, andlastUpdatedAtfields when the UI depends on them. - Reset stale errors before retrying an async action.
- Keep subscriptions, intervals, sockets, and browser listeners outside stores unless the store owns their lifecycle and cleanup.
SSR, Nuxt, and Router Guards
- In SSR contexts, use the store inside setup, getters, or actions so Pinia can resolve the active app instance.
- When using a store outside setup, such as in a router guard, pass the active Pinia instance if the framework requires it.
- Do not read browser-only storage during server rendering.
- In Nuxt, prefer the Nuxt Pinia integration and SSR-safe composables for data fetching.
- Avoid singleton state leaks across requests by relying on the framework-created Pinia instance.
Persistence
- Persist only the fields that must survive reloads, such as preferences or incomplete local drafts.
- Never persist secrets, access tokens, refresh tokens, raw PII, or authorization decisions in browser storage.
- Use field allowlists and versioned migrations for persisted store schemas.
- Treat persisted state as untrusted input and validate it before using it for critical workflows.
- Account for hydration timing before rendering UI that depends on persisted values.
Testing and Tooling
- Use
@pinia/testingfor component tests that need stores. - Test store actions directly for domain behavior and edge cases.
- Reset Pinia between tests to avoid shared state leakage.
- Add HMR support with
acceptHMRUpdate()in stores when the project uses Vite HMR patterns. - Keep stores easy to inspect in Vue Devtools by using clear state names and focused stores.
Anti-Patterns
- Do not use Pinia as a dumping ground for every reactive value.
- Do not destructure state directly from a store without
storeToRefs(). - Do not mutate props or route objects through store actions.
- Do not put server-only objects, request instances, DOM nodes, or timers in store state.
- Do not create circular reads between stores in setup functions; compose stores through actions or computed values instead.
Related rules
Senior full-stack TypeScript, React, Node.js guidance with clean architecture, testing, and WHY-oriented reasoning.
Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.
Android development with Jetpack Compose, clean architecture, and Material Design 3.
Angular development with Novo Elements UI library using standalone components.
Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.
Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.