Pinia — Official Vue.js State Management
You are an expert in Pinia, the official state management library for Vue.js. You help developers build Vue applications with type-safe stores, Composition API support, getters (computed), actions (sync and async), plugins, SSR compatibility, and Vue DevTools integration — replacing Vuex with a simpler, fully typed, modular store system.
Core Capabilities
Store Definition
// stores/auth.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
// Setup Store (Composition API style)
export const useAuthStore = defineStore("auth", () => {
const user = ref<User | null>(null);
const token = ref<string | null>(localStorage.getItem("token"));
const isLoading = ref(false);
// Getters (computed)
const isAuthenticated = computed(() => !!token.value);
const isAdmin = computed(() => user.value?.role === "admin");
// Actions
async function login(email: string, password: string) {
isLoading.value = true;
try {
const response = await api.post("/auth/login", { email, password });
token.value = response.data.token;
user.value = response.data.user;
localStorage.setItem("token", token.value!);
} finally {
isLoading.value = false;
}
}
function logout() {
token.value = null;
user.value = null;
localStorage.removeItem("token");
}
async function fetchProfile() {
if (!token.value) return;
user.value = await api.get("/auth/profile");
}
return { user, token, isLoading, isAuthenticated, isAdmin, login, logout, fetchProfile };
});
// Option Store style (familiar to Vuex users)
export const useCartStore = defineStore("cart", {
state: () => ({
items: [] as CartItem[],
}),
getters: {
total: (state) => state.items.reduce((sum, i) => sum + i.price * i.qty, 0),
itemCount: (state) => state.items.reduce((sum, i) => sum + i.qty, 0),
},
actions: {
addItem(product: Product) {
const existing = this.items.find(i => i.id === product.id);
if (existing) existing.qty++;
else this.items.push({ ...product, qty: 1 });
},
removeItem(id: string) {
this.items = this.items.filter(i => i.id !== id);
},
async checkout() {
await api.post("/orders", { items: this.items });
this.items = [];
},
},
});
Usage in Components
<script setup lang="ts">
import { useAuthStore } from "@/stores/auth";
import { useCartStore } from "@/stores/cart";
import { storeToRefs } from "pinia";
const auth = useAuthStore();
const cart = useCartStore();
// storeToRefs preserves reactivity for destructured state/getters
const { user, isAuthenticated } = storeToRefs(auth);
const { total, itemCount } = storeToRefs(cart);
</script>
<template>
<nav>
<span v-if="isAuthenticated">{{ user?.name }}</span>
<button v-else @click="auth.login(email, password)">Login</button>
<span>Cart ({{ itemCount }}): ${{ total.toFixed(2) }}</span>
</nav>
</template>
Installation
npm install pinia
// main.ts
import { createPinia } from "pinia";
app.use(createPinia());
Best Practices
- Setup stores — Prefer Composition API style (
ref, computed); full TypeScript inference without extra types
- storeToRefs — Use
storeToRefs(store) when destructuring; plain destructure breaks reactivity
- One store per domain — Separate auth, cart, ui stores; avoid a single monolithic store
- Actions for async — Put API calls in actions; components stay clean, logic is reusable and testable
- Getters for derived — Use computed/getters for filtered lists, totals, formatted values; auto-cached
- Plugins — Use plugins for persistence (
pinia-plugin-persistedstate), logging, or shared behaviors
- Store composition — Import other stores inside a store:
const auth = useAuthStore(); explicit dependencies
- DevTools — Pinia integrates with Vue DevTools; inspect state, time-travel, edit state in real-time
1---2name: pinia3description: You are an expert in Pinia, the official state management library for Vue.js. You help developers build Vue applications with type-safe stores, Composition API support, getters (computed), actions (sync and async), plugins, SSR compatibility, and Vue DevTools integration — replacing Vuex with a simpler, fully typed, modular store system.4license: Apache-2.05---67# Pinia — Official Vue.js State Management89You are an expert in Pinia, the official state management library for Vue.js. You help developers build Vue applications with type-safe stores, Composition API support, getters (computed), actions (sync and async), plugins, SSR compatibility, and Vue DevTools integration — replacing Vuex with a simpler, fully typed, modular store system.1011## Core Capabilities1213### Store Definition1415```typescript16// stores/auth.ts17import { defineStore } from "pinia";18import { ref, computed } from "vue";1920// Setup Store (Composition API style)21export const useAuthStore = defineStore("auth", () => {22 const user = ref<User | null>(null);23 const token = ref<string | null>(localStorage.getItem("token"));24 const isLoading = ref(false);2526 // Getters (computed)27 const isAuthenticated = computed(() => !!token.value);28 const isAdmin = computed(() => user.value?.role === "admin");2930 // Actions31 async function login(email: string, password: string) {32 isLoading.value = true;33 try {34 const response = await api.post("/auth/login", { email, password });35 token.value = response.data.token;36 user.value = response.data.user;37 localStorage.setItem("token", token.value!);38 } finally {39 isLoading.value = false;40 }41 }4243 function logout() {44 token.value = null;45 user.value = null;46 localStorage.removeItem("token");47 }4849 async function fetchProfile() {50 if (!token.value) return;51 user.value = await api.get("/auth/profile");52 }5354 return { user, token, isLoading, isAuthenticated, isAdmin, login, logout, fetchProfile };55});5657// Option Store style (familiar to Vuex users)58export const useCartStore = defineStore("cart", {59 state: () => ({60 items: [] as CartItem[],61 }),62 getters: {63 total: (state) => state.items.reduce((sum, i) => sum + i.price * i.qty, 0),64 itemCount: (state) => state.items.reduce((sum, i) => sum + i.qty, 0),65 },66 actions: {67 addItem(product: Product) {68 const existing = this.items.find(i => i.id === product.id);69 if (existing) existing.qty++;70 else this.items.push({ ...product, qty: 1 });71 },72 removeItem(id: string) {73 this.items = this.items.filter(i => i.id !== id);74 },75 async checkout() {76 await api.post("/orders", { items: this.items });77 this.items = [];78 },79 },80});81```8283### Usage in Components8485```vue86<script setup lang="ts">87import { useAuthStore } from "@/stores/auth";88import { useCartStore } from "@/stores/cart";89import { storeToRefs } from "pinia";9091const auth = useAuthStore();92const cart = useCartStore();9394// storeToRefs preserves reactivity for destructured state/getters95const { user, isAuthenticated } = storeToRefs(auth);96const { total, itemCount } = storeToRefs(cart);97</script>9899<template>100 <nav>101 <span v-if="isAuthenticated">{{ user?.name }}</span>102 <button v-else @click="auth.login(email, password)">Login</button>103 <span>Cart ({{ itemCount }}): ${{ total.toFixed(2) }}</span>104 </nav>105</template>106```107108## Installation109110```bash111npm install pinia112```113114```typescript115// main.ts116import { createPinia } from "pinia";117app.use(createPinia());118```119120## Best Practices1211221. **Setup stores** — Prefer Composition API style (`ref`, `computed`); full TypeScript inference without extra types1232. **storeToRefs** — Use `storeToRefs(store)` when destructuring; plain destructure breaks reactivity1243. **One store per domain** — Separate auth, cart, ui stores; avoid a single monolithic store1254. **Actions for async** — Put API calls in actions; components stay clean, logic is reusable and testable1265. **Getters for derived** — Use computed/getters for filtered lists, totals, formatted values; auto-cached1276. **Plugins** — Use plugins for persistence (`pinia-plugin-persistedstate`), logging, or shared behaviors1287. **Store composition** — Import other stores inside a store: `const auth = useAuthStore()`; explicit dependencies1298. **DevTools** — Pinia integrates with Vue DevTools; inspect state, time-travel, edit state in real-time