LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 3317 min read

State Management with Pinia

Share state across unrelated components cleanly using Pinia, Vue's official state management library.

Why a State Management Library

provide/inject solves sharing state within one component subtree. But state that many unrelated parts of an app need — a logged-in user, a shopping cart, a theme preference — needs a more global, structured solution. Pinia is Vue's official answer.

Installing Pinia

npm install pinia
// src/main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
createApp(App).use(createPinia()).mount('#app');

Defining a Store

A store is defined with defineStore, and — using the "setup store" syntax — looks almost exactly like a composable, using ref() for state and plain functions for actions.

// stores/cart.js
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
export const useCartStore = defineStore('cart', () => {
const items = ref([]);
const totalPrice = computed(() =>
items.value.reduce((sum, item) => sum + item.price, 0)
);
function addItem(item) {
items.value.push(item);
}
function removeItem(id) {
items.value = items.value.filter((item) => item.id !== id);
}
return { items, totalPrice, addItem, removeItem };
});

Using a Store in a Component

Any component calls the store's composable-style function to get access to its shared, reactive state — every component calling useCartStore() gets the same underlying store instance.

<script setup>
import { useCartStore } from '../stores/cart';
const cart = useCartStore();
</script>
<template>
<p>Total: {{ cart.totalPrice }}</p>
<ul>
<li v-for="item in cart.items" :key="item.id">
{{ item.name }}
<button @click="cart.removeItem(item.id)">Remove</button>
</li>
</ul>
</template>
One Store, Every Component Shares It

Unlike a composable called fresh in each component (which creates independent state), a Pinia store is a singleton per app — calling useCartStore() anywhere returns the exact same shared instance.

Common Beginner Mistakes

Destructuring reactive state directly from a store

const { items } = cart loses reactivity, exactly like destructuring a plain reactive() object — use storeToRefs(cart) if you need to destructure while keeping reactivity.

Putting every piece of app state into Pinia

Local component state (like whether a dropdown is open) usually belongs in the component itself with ref() — reserve Pinia for state genuinely shared across multiple, unrelated components.

FAQs

Pinia is the official successor to Vuex — it's simpler, has better TypeScript support, and is the recommended state library for all new Vue 3 projects.

Yes — most apps have several, one per feature/domain (a cart store, a user store, a settings store), rather than one giant combined store.

Summary

Pinia gives you a shared, singleton store per concern, defined with syntax that closely mirrors a composable. Next, you'll look at organizing larger stores with actions and getters more formally.

Next Lesson →

Pinia Actions, Getters & Stores