Day 3 of 5
⏱ ~60 minutes
Vue.js in 5 Days — Day 3

Pinia State Management

When multiple components need the same data, passing props gets messy. Pinia is Vue's official state management library — simpler than Vuex, works great with the Composition API. This lesson shows you how to build stores and share state across your whole app.

Why You Need a Store

Imagine a shopping cart. The cart count shows in the navbar. The checkout button shows in the sidebar. The cart items list shows in the main view. All three components need the same data. You could pass it down through props, but that gets unwieldy fast (called 'prop drilling').

A store is a single place where shared state lives. Any component can read from it or write to it directly. Pinia is the official Vue store — it's lightweight, TypeScript-friendly, and integrates with Vue DevTools.

Creating Your First Pinia Store

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

const app = createApp(App)
app.use(createPinia())  // register Pinia with your app
app.mount('#app')
src/stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// defineStore takes an ID (must be unique) and a setup function
export const useCartStore = defineStore('cart', () => {
  // state: reactive variables
  const items = ref([])

  // getters: computed properties
  const totalItems = computed(() =>
    items.value.reduce((sum, item) => sum + item.qty, 0)
  )
  const totalPrice = computed(() =>
    items.value.reduce((sum, item) => sum + item.price * item.qty, 0)
  )

  // actions: functions that modify state
  function addItem(product) {
    const existing = items.value.find(i => i.id === product.id)
    if (existing) {
      existing.qty++
    } else {
      items.value.push({ ...product, qty: 1 })
    }
  }

  function removeItem(productId) {
    items.value = items.value.filter(i => i.id !== productId)
  }

  function clearCart() {
    items.value = []
  }

  // Return everything the component can access
  return { items, totalItems, totalPrice, addItem, removeItem, clearCart }
})

Using the Store in Components

CartIcon.vue (reads from store)


ProductCard.vue (writes to store)


💡
You can call useCartStore() in multiple components and they all share the exact same instance. Change it in one component and every other component that uses it updates automatically. That's the whole point.

Async Actions

Pinia actions can be async — just add async to the function. This is where you'd put API calls.

src/stores/users.js
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useUserStore = defineStore('users', () => {
  const users = ref([])
  const loading = ref(false)
  const error = ref(null)

  async function fetchUsers() {
    loading.value = true
    error.value = null
    try {
      const res = await fetch('https://jsonplaceholder.typicode.com/users')
      users.value = await res.json()
    } catch (e) {
      error.value = 'Failed to load users'
    } finally {
      loading.value = false
    }
  }

  return { users, loading, error, fetchUsers }
})
UserList.vue


📝 Day 3 Exercise
Build a Shopping Cart
  1. Create a Pinia store for a shopping cart with items, add, remove, and clear actions.
  2. Build a ProductList.vue component with 6 hardcoded products and an 'Add to Cart' button on each.
  3. Build a CartSidebar.vue that shows all items in the cart with quantity and price.
  4. Add a cart item count badge to a header/navbar component — it should update live as items are added.
  5. Add a 'Total' computed getter to the store and display it in the sidebar.
  6. Add a 'Remove' button per cart item and a 'Clear Cart' button.

Day 3 Summary

  • Pinia stores hold shared state that multiple components need to access.
  • A store has three parts: state (refs), getters (computed), and actions (functions).
  • Call useYourStore() in any component. They all share the same reactive instance.
  • Actions can be async — put your API calls there and track loading/error state in the store.
  • With Pinia's setup store syntax, it feels exactly like <script setup>.
Finished this lesson?