Free 40-page Claude guide — setup, 120 prompt codes, MCP servers, AI agents. Download free →
CLSkills
Vue.jsintermediateNew

Vue Pinia Global State

Share

Manage global app state in Vue 3 with Pinia (modern Vuex replacement)

Works with OpenClaude

You are the #1 Vue 3 state management expert from Silicon Valley — the architect that companies use when their app outgrows component-local state. The user wants to set up Pinia for global state in their Vue 3 app.

What to check first

  • Vue 3 + Pinia installed
  • Identify what state should be global vs component-local
  • Decide on store structure: by feature or by data type

Steps

  1. npm install pinia
  2. Create the Pinia instance in main.ts and use it
  3. Define stores with defineStore('storeId', { state, getters, actions })
  4. Use store in components via useUserStore()
  5. Reactive state should always go through state(), not as raw values
  6. For async operations, use actions

Code

// main.ts
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');

// stores/user.ts
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    user: null as User | null,
    loading: false,
    error: null as Error | null,
  }),

  getters: {
    isAuthenticated: (state) => state.user !== null,
    fullName: (state) => state.user ? `${state.user.firstName} ${state.user.lastName}` : '',
  },

  actions: {
    async login(email: string, password: string) {
      this.loading = true;
      this.error = null;
      try {
        const response = await fetch('/api/login', {
          method: 'POST',
          body: JSON.stringify({ email, password }),
        });
        if (!response.ok) throw new Error('Login failed');
        this.user = await response.json();
      } catch (err) {
        this.error = err as Error;
        throw err;
      } finally {
        this.loading = false;
      }
    },

    logout() {
      this.user = null;
    },
  },
});

// stores/cart.ts — composition API style
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([]);

  const total = computed(() =>
    items.value.reduce((sum, item) => sum + item.price * item.qty, 0)
  );

  const itemCount = computed(() =>
    items.value.reduce((sum, item) => sum + item.qty, 0)
  );

  function add(item: CartItem) {
    const existing = items.value.find((i) => i.id === item.id);
    if (existing) {
      existing.qty += item.qty;
    } else {
      items.value.push(item);
    }
  }

  function remove(id: string) {
    items.value = items.value.filter((i) => i.id !== id);
  }

  function clear() {
    items.value = [];
  }

  return { items, total, itemCount, add, remove, clear };
});

// In a component
<script setup>
import { useUserStore } from '@/stores/user';
import { useCartStore } from '@/stores/cart';
import { storeToRefs } from 'pinia';

const userStore = useUserStore();
const cartStore = useCartStore();

// Destructure with reactivity preserved
const { user, loading } = storeToRefs(userStore);

// Actions can be destructured directly (they're not reactive)
const { login, logout } = userStore;
</script>

<template>
  <div v-if="user">{{ user.email }}</div>
  <button @click="logout">Logout</button>
  <div>Cart: {{ cartStore.itemCount }} items, ${{ cartStore.total }}</div>
</template>

Common Pitfalls

  • Destructuring state directly without storeToRefs — loses reactivity
  • Mutating arrays/objects directly inside getters
  • Calling stores in script setup before app.use(pinia) — error
  • Storing non-serializable data (Date objects, functions) — breaks devtools

When NOT to Use This Skill

  • For component-local state — use ref() in setup
  • For URL state — use vue-router instead

How to Verify It Worked

  • Use Vue DevTools to inspect Pinia stores
  • Test that state changes trigger component updates

Production Considerations

  • Persist stores to localStorage with pinia-plugin-persistedstate
  • Use TypeScript for type-safe state
  • Don't put HTTP responses directly in state — transform first

Quick Info

CategoryVue.js
Difficultyintermediate
Version1.0.0
AuthorClaude Skills Hub
vuepiniastate-management

Install command:

Want a Vue.js skill personalized to YOUR project?

This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.