0Pricing
Frontend Academy · Lesson

Pinia for Vue: defineStore and storeToRefs

Define a Pinia store with defineStore, access it in components, use storeToRefs to keep reactivity when destructuring state.

Pinia for Vue: defineStore and storeToRefs is a free Frontend Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is Pinia?

Pinia is the official state management library for Vue 3. It replaces Vuex with a simpler, type-safe, DevTools-integrated API that feels like Composition API.

Installing Pinia

Install and register the Pinia plugin with the Vue app.

npm install pinia

// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

const app = createApp(App);
app.use(createPinia());
app.mount('#app');

defineStore — Composition API Style

defineStore creates a store. The Composition API style uses ref, computed, and functions directly — just like a composable.

// stores/counter.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  const doubled = computed(() => count.value * 2);
  function increment() { count.value++; }
  return { count, doubled, increment };
});

defineStore — Options Style

The Options style mirrors Vue's Options API: state() for reactive data, getters (like computed), and actions (like methods).

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    doubled: (state) => state.count * 2,
  },
  actions: {
    increment() { this.count++; },
    async fetchAndSet(id: string) {
      this.count = await fetchCount(id);
    }
  }
});

Using a Store in a Component

Call the store hook inside setup(). The store is a reactive object — you can read state and call actions directly.

<script setup>
import { useCounterStore } from '@/stores/counter';

const store = useCounterStore();
</script>

<template>
  <p>{{ store.count }}</p>
  <button @click="store.increment">+</button>
</template>

storeToRefs — Safe Destructuring

Direct destructuring of a store loses reactivity for state and getters (they become plain values). Use storeToRefs() to destructure reactively.

import { storeToRefs } from 'pinia';

const store = useCounterStore();

// Actions can be destructured normally (they're functions):
const { increment } = store;

// State and getters need storeToRefs:
const { count, doubled } = storeToRefs(store);

$patch — Batch Updates

store.$patch() applies multiple state changes as one operation. Useful for batching related updates.

store.$patch({
  count: 10,
  name: 'Updated'
});

// Or with a function for complex logic:
store.$patch(state => {
  state.items.push(newItem);
  state.total += newItem.price;
});

$subscribe — Watch Store Changes

store.$subscribe(callback) reacts to any store mutation, similar to Vue's watch but for the whole store.

Pinia Plugins

Pinia plugins extend every store. Common use: persist state to localStorage, add debug logging, or inject services.

import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

Pinia DevTools

Pinia integrates with Vue DevTools. You can inspect store state, see which actions were called, and time-travel between state snapshots.

Multiple Stores

Create one store per domain (useAuthStore, useCartStore, useNotificationStore). Stores can import and use each other. Pinia's flat structure (no nested modules) makes this simple.

Quick Check

Why should you use storeToRefs() when destructuring a Pinia store?

Recap: Pinia

defineStore creates stores. Composition or Options API style. Access with useXxxStore() hook. Use storeToRefs() for reactive destructuring. $patch() for batched updates. Plugins for persist and other extensions. DevTools integration. One store per domain — no Vuex-style nested modules.

Frequently asked questions

Is the “Pinia for Vue: defineStore and storeToRefs” lesson free?

Yes — the full text of “Pinia for Vue: defineStore and storeToRefs” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “Pinia for Vue: defineStore and storeToRefs”?

Define a Pinia store with defineStore, access it in components, use storeToRefs to keep reactivity when destructuring state. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Pinia for Vue: defineStore and storeToRefs” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Redux Toolkit: createSlice and configureStore
  2. Zustand for Lightweight React State
  3. Pinia for Vue: defineStore and storeToRefs
  4. When to Use Global vs Local State
← Back to Frontend Academy