Files
inkreach-official-website/.agents/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md
T

6.2 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Use Pinia for Large-Scale Vue Applications MEDIUM Hand-rolled reactive stores lack DevTools integration, TypeScript support, and debugging capabilities needed for production apps best-practice
vue3
pinia
state-management
devtools
architecture
scalability

Use Pinia for Large-Scale Vue Applications

Impact: MEDIUM - While Vue's Composition API allows creating simple reactive stores with reactive() and ref(), these hand-rolled solutions lack the tooling, conventions, and debugging capabilities needed for large-scale production applications. Pinia is the official Vue state management solution and should be used for any non-trivial application.

Task Checklist

  • Use Pinia for applications with shared state across multiple components
  • Use Pinia when team collaboration requires consistent patterns
  • Use Pinia when you need Vue DevTools state debugging
  • Hand-rolled reactive() is acceptable only for simple, single-developer apps
  • Migrate from Vuex to Pinia (Vuex is in maintenance mode)

When Hand-Rolled State is Acceptable

Simple reactive state is fine for:

  • Prototypes and proof-of-concepts
  • Very small applications with minimal shared state
  • Single-developer projects with limited scope
  • Learning purposes
// Simple hand-rolled store - OK for small apps
import { reactive, readonly } from 'vue'

export const store = reactive({
  count: 0,
  increment() {
    this.count++
  }
})

When to Use Pinia

Use Pinia when you have any of these requirements:

1. DevTools Integration

Pinia provides rich Vue DevTools support:

  • Timeline of state changes
  • State inspection and editing
  • Time-travel debugging
  • Action tracking
// Pinia store - fully visible in DevTools
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++  // Tracked in DevTools timeline
    }
  }
})

2. TypeScript Support

Pinia has excellent TypeScript inference:

// Full type inference without extra configuration
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    age: 0,
    preferences: {
      theme: 'light' as 'light' | 'dark'
    }
  }),

  getters: {
    // Return type is inferred
    displayName: (state) => state.name || 'Anonymous'
  },

  actions: {
    // Full parameter type checking
    setUser(name: string, age: number) {
      this.name = name
      this.age = age
    }
  }
})

// Usage is fully typed
const userStore = useUserStore()
userStore.name  // string
userStore.displayName  // string
userStore.setUser('John', 30)  // Type-checked

3. Team Collaboration

Pinia enforces conventions that help teams:

// Consistent structure across all stores
// stores/products.js
export const useProductsStore = defineStore('products', {
  state: () => ({ /* ... */ }),
  getters: { /* ... */ },
  actions: { /* ... */ }
})

// stores/cart.js - Same structure
export const useCartStore = defineStore('cart', {
  state: () => ({ /* ... */ }),
  getters: { /* ... */ },
  actions: { /* ... */ }
})

4. Hot Module Replacement (HMR)

Pinia supports HMR out of the box - state persists during development:

// State survives code changes during development
// No need to re-login or re-create state after every edit

5. Server-Side Rendering (SSR)

Pinia handles SSR state correctly:

// Automatic per-request state isolation
// State serialization for hydration
// No cross-request pollution

6. Plugin Ecosystem

Pinia supports plugins for common needs:

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

// Now stores can persist to localStorage
export const useSettingsStore = defineStore('settings', {
  state: () => ({
    theme: 'light',
    language: 'en'
  }),
  persist: true  // Automatically saved/restored
})

Pinia vs Hand-Rolled Comparison

Feature Hand-Rolled reactive() Pinia
DevTools integration No Yes
TypeScript inference Manual Automatic
HMR support No Yes
SSR support Manual Built-in
Plugins No Yes
Time-travel debugging No Yes
Learning curve Lower Slightly higher
Bundle size Smaller ~1KB
Team conventions None Enforced

Migration from Vuex

Vuex is now in maintenance mode. Migrate to Pinia for new features:

// Vuex (legacy)
export default createStore({
  state: { count: 0 },
  mutations: {
    INCREMENT(state) { state.count++ }
  },
  actions: {
    increment({ commit }) { commit('INCREMENT') }
  }
})

// Pinia (recommended)
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() { this.count++ }  // No mutations needed!
  }
})

Pinia advantages over Vuex:

  • No mutations (simpler mental model)
  • Better TypeScript support
  • No nested modules complexity
  • Smaller bundle size
  • Composition API style available

Pinia Store Styles

Choose the style that fits your team:

Options Style (Similar to Options API)

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2
  },
  actions: {
    increment() { this.count++ }
  }
})

Setup Style (Composition API)

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  function increment() { count.value++ }

  return { count, double, increment }
})

Quick Start

npm install pinia
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

Reference