One File Per Domain
Modular Stores
Split stores by domain into separate files under stores/. Compose them by importing one from another — no global tree, no nesting.
What you'll learn
- Organize stores by domain (auth, cart, users)
- Compose stores by calling one from another
- Type the returned store automatically
Vuex had a global tree with namespaced modules. Pinia is flatter — each store is an independent file. Compose them by importing.
File Layout
src/stores/
auth.ts
cart.ts
products.ts Store Per Domain
// stores/auth.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useAuthStore = defineStore('auth', () => {
const userId = ref<string | null>(null)
const isLoggedIn = computed(() => userId.value !== null)
function login(id: string) { userId.value = id }
function logout() { userId.value = null }
return { userId, isLoggedIn, login, logout }
}) Composing Stores
One store can use another — just call its use…Store() inside.
// stores/cart.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useAuthStore } from './auth'
export const useCartStore = defineStore('cart', () => {
const auth = useAuthStore()
const items = ref<{ id: string; qty: number }[]>([])
const canCheckout = computed(() => auth.isLoggedIn && items.value.length > 0)
function add(id: string) {
items.value.push({ id, qty: 1 })
}
return { items, canCheckout, add }
}) Typing
Setup-style stores infer everything. The return type of useCartStore() is automatically { items, canCheckout, add } with full types — no manual generics needed.