Vue 3 Pinia State Management

frontend
TypeScript
architecture
zen
Remix

Modern state management in Vue 3 using Pinia with TypeScript and composition API.

12/8/2025

Prompt

Vue 3 Pinia State Management

Implement global state management for [Application] using Pinia with TypeScript and Composition API.

Requirements

1. Store Structure

Create Pinia stores for:

  • [Store 1] - [Data type] (e.g., User/Auth store)
  • [Store 2] - [Data type] (e.g., Products/Cart store)
  • [Store 3] - [Data type] (e.g., UI state store)

2. Store Features

Implement for each store:

  • State - Reactive data properties
  • Getters - Computed derived values
  • Actions - Business logic and async operations
  • TypeScript types - Full type safety

3. State Operations

Handle:

  • Reading state values
  • Updating state
  • Async data fetching
  • Resetting state
  • State persistence (if needed)

4. Store Organization

Structure:

src/stores/
  ├── [domain].ts      (e.g., user.ts)
  ├── [domain].ts      (e.g., products.ts)
  └── index.ts         (exports)

5. Integration

Set up:

  • Pinia plugin installation
  • DevTools support
  • Hot module replacement
  • Server-side rendering (if applicable)

Implementation Pattern

// stores/[storeName].ts
import { defineStore } from 'pinia'
import type { [Type] } from '@/types'

export const use[StoreName]Store = defineStore('[storeName]', {
  // State
  state: () => ({
    [property1]: [initialValue] as [Type],
    [property2]: [] as [Type][],
    [isLoading]: false,
    [error]: null as string | null
  }),
  
  // Getters (computed values)
  getters: {
    [computedName]: (state) => {
      return /* derive from state */
    },
    [filteredItems]: (state) => {
      return state.[items].filter(/* condition */)
    },
    // Getter with argument
    [getItemById]: (state) => {
      return (id: string) => state.[items].find(item => item.id === id)
    }
  },
  
  // Actions (methods)
  actions: {
    async fetch[Data]() {
      this.[isLoading] = true
      this.[error] = null
      
      try {
        const data = await api.get[Data]()
        this.[property] = data
      } catch (error) {
        this.[error] = error.message
      } finally {
        this.[isLoading] = false
      }
    },
    
    add[Item](item: [Type]) {
      this.[items].push(item)
    },
    
    update[Item](id: string, updates: Partial<[Type]>) {
      const index = this.[items].findIndex(item => item.id === id)
      if (index !== -1) {
        this.[items][index] = { ...this.[items][index], ...updates }
      }
    },
    
    remove[Item](id: string) {
      const index = this.[items].findIndex(item => item.id === id)
      if (index !== -1) {
        this.[items].splice(index, 1)
      }
    },
    
    reset() {
      this.$reset() // Reset to initial state
    }
  }
})

// Alternative: Composition API style (Setup Stores)
import { ref, computed } from 'vue'

export const use[StoreName]Store = defineStore('[storeName]', () => {
  // State
  const [property] = ref<[Type]>([initialValue])
  const [items] = ref<[Type][]>([])
  
  // Getters
  const [computed] = computed(() => {
    return /* derive from state */
  })
  
  // Actions
  async function fetch[Data]() {
    const data = await api.get[Data]()
    [property].value = data
  }
  
  function add[Item](item: [Type]) {
    [items].value.push(item)
  }
  
  return {
    // Expose state
    [property],
    [items],
    // Expose getters
    [computed],
    // Expose actions
    fetch[Data],
    add[Item]
  }
})

// main.ts - Plugin setup
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

// Component usage (Composition API)
<script setup lang=\"ts\">
import { use[StoreName]Store } from '@/stores/[storeName]'
import { storeToRefs } from 'pinia'

const [storeName]Store = use[StoreName]Store()

// Get reactive references
const { [property], [computed] } = storeToRefs([storeName]Store)

// Access actions directly
[storeName]Store.fetch[Data]()

// Or destructure actions
const { add[Item], remove[Item] } = [storeName]Store
</script>

<template>
  <div>
    <p>{{ [property] }}</p>
    <button @click=\"add[Item]([item])\">Add</button>
  </div>
</template>

// Component usage (Options API)
<script lang=\"ts\">
import { mapState, mapActions } from 'pinia'
import { use[StoreName]Store } from '@/stores/[storeName]'

export default {
  computed: {
    ...mapState(use[StoreName]Store, ['[property]', '[computed]'])
  },
  methods: {
    ...mapActions(use[StoreName]Store, ['fetch[Data]', 'add[Item]'])
  }
}
</script>

State Persistence

import { defineStore } from 'pinia'

export const use[StoreName]Store = defineStore('[storeName]', {
  state: () => ({
    [property]: localStorage.getItem('[key]') || [default]
  }),
  actions: {
    update[Property](value: [Type]) {
      this.[property] = value
      localStorage.setItem('[key]', value)
    }
  }
})

Best Practices

  • Use Options API style for simple stores
  • Use Composition API style for complex logic
  • Always use TypeScript for type safety
  • Use storeToRefs() for reactive destructuring
  • Keep stores focused and single-purpose
  • Use getters for computed values
  • Actions can call other store actions
  • Use $reset() to reset state
  • Leverage Pinia DevTools for debugging

Tags

vue
pinia
state-management
typescript

Tested Models

gpt-4
claude-3-5-sonnet

Comments (0)

Sign in to leave a comment

Sign In
\n\n\n\n// Component usage (Options API)\n\n```\n\n## State Persistence\n\n```typescript\nimport { defineStore } from 'pinia'\n\nexport const use[StoreName]Store = defineStore('[storeName]', {\n state: () => ({\n [property]: localStorage.getItem('[key]') || [default]\n }),\n actions: {\n update[Property](value: [Type]) {\n this.[property] = value\n localStorage.setItem('[key]', value)\n }\n }\n})\n```\n\n## Best Practices\n- Use Options API style for simple stores\n- Use Composition API style for complex logic\n- Always use TypeScript for type safety\n- Use storeToRefs() for reactive destructuring\n- Keep stores focused and single-purpose\n- Use getters for computed values\n- Actions can call other store actions\n- Use $reset() to reset state\n- Leverage Pinia DevTools for debugging"}