Getters in Pinia are the equivalent of computed properties. They derive values from state and are cached until their dependencies change.
Basic Getters
Options Syntax
typescriptimport { defineStore } from 'pinia' export const useCartStore = defineStore('cart', { state: () => ({ items: [] as CartItem[], taxRate: 0.1 }), getters: { // Simple getter with state parameter itemCount: (state) => state.items.length, // Getter returning a value subtotal: (state) => state.items.reduce((sum, item) => sum + item.price * item.quantity, 0), // Using this to access other getters tax(): number { return this.subtotal * this.taxRate }, // Using other getters total(): number { return this.subtotal + this.tax }, // Getter that returns a function (for parameters) getItemById: (state) => { return (id: number) => state.items.find(item => item.id === id) } } })
Setup Syntax
typescriptimport { ref, computed } from 'vue' import { defineStore } from 'pinia' export const useCartStore = defineStore('cart', () => { const items = ref<CartItem[]>([]) const taxRate = ref(0.1) // Getters as computed const itemCount = computed(() => items.value.length) const subtotal = computed(() => items.value.reduce((sum, item) => sum + item.price * item.quantity, 0) ) const tax = computed(() => subtotal.value * taxRate.value) const total = computed(() => subtotal.value + tax.value) // Function getter function getItemById(id: number) { return items.value.find(item => item.id === id) } return { items, taxRate, itemCount, subtotal, tax, total, getItemById } })
Accessing Other Stores in Getters
typescriptimport { defineStore } from 'pinia' import { useUserStore } from './user' export const useCartStore = defineStore('cart', { state: () => ({ items: [] as CartItem[] }), getters: { // Access another store discountedTotal(): number { const userStore = useUserStore() const discount = userStore.isPremium ? 0.1 : 0 return this.total * (1 - discount) } } })
Parameterized Getters
Getters can't take parameters directly, but can return functions:
typescriptexport const useProductStore = defineStore('products', { state: () => ({ products: [] as Product[] }), getters: { // Returns a function that takes parameters getByCategory: (state) => { return (category: string) => state.products.filter(p => p.category === category) }, // Price range filter getByPriceRange: (state) => { return (min: number, max: number) => state.products.filter(p => p.price >= min && p.price <= max) }, // Search search: (state) => { return (query: string) => { const lower = query.toLowerCase() return state.products.filter(p => p.name.toLowerCase().includes(lower) || p.description.toLowerCase().includes(lower) ) } } } }) // Usage const store = useProductStore() const electronics = store.getByCategory('electronics') const affordable = store.getByPriceRange(0, 50) const results = store.search('laptop')
Note: Parameterized getters are NOT cached!
Getter Caching
typescriptgetters: { // ✅ Cached - recalculates only when items change itemCount: (state) => { console.log('Computing itemCount') // Logs once per dependency change return state.items.length }, // ❌ NOT cached - runs on every call getExpensiveItems: (state) => (minPrice: number) => { console.log('Filtering items') // Logs on every call return state.items.filter(i => i.price > minPrice) } }
Complex Getter Example
typescriptexport const useAnalyticsStore = defineStore('analytics', { state: () => ({ orders: [] as Order[] }), getters: { // Group by month ordersByMonth(): Record<string, Order[]> { return this.orders.reduce((acc, order) => { const month = order.date.toISOString().slice(0, 7) if (!acc[month]) acc[month] = [] acc[month].push(order) return acc }, {} as Record<string, Order[]>) }, // Monthly totals monthlyTotals(): Record<string, number> { const result: Record<string, number> = {} for (const [month, orders] of Object.entries(this.ordersByMonth)) { result[month] = orders.reduce((sum, o) => sum + o.total, 0) } return result }, // Best performing month bestMonth(): { month: string; total: number } | null { const entries = Object.entries(this.monthlyTotals) if (entries.length === 0) return null return entries.reduce((best, [month, total]) => total > best.total ? { month, total } : best , { month: '', total: 0 }) } } })