Pinia is Vue’s official state management library. It replaces Vuex with a simpler API that works naturally with the Composition API.

Installation

  npm install pinia
  

Register in 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');
  

Define a Store

stores/counter.js:

  import { ref, computed } from 'vue';
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  const doubleCount = computed(() => count.value * 2);

  function increment() {
    count.value++;
  }

  function reset() {
    count.value = 0;
  }

  return { count, doubleCount, increment, reset };
});
  

This is the Setup Store syntax — mirrors Composition API patterns.

Use a Store in Components

  <script setup>
import { useCounterStore } from '../stores/counter';
import { storeToRefs } from 'pinia';

const store = useCounterStore();

// Destructure reactive state with storeToRefs
const { count, doubleCount } = storeToRefs(store);

// Actions can be destructured directly
const { increment, reset } = store;
</script>

<template>
  <p>Count: {{ count }} (double: {{ doubleCount }})</p>
  <button @click="increment">+1</button>
  <button @click="reset">Reset</button>
</template>
  

User Store Example

stores/user.js:

  import { ref } from 'vue';
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', () => {
  const user = ref(null);
  const isLoggedIn = ref(false);

  async function login(email, password) {
    const res = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    });
    user.value = await res.json();
    isLoggedIn.value = true;
  }

  function logout() {
    user.value = null;
    isLoggedIn.value = false;
  }

  return { user, isLoggedIn, login, logout };
});
  

When to Use Pinia

Use Pinia Use local state
Auth user across routes Form input on one page
Shopping cart Toggle visibility
Shared theme/settings Component-specific UI state

Next: extract reusable logic into composables.