Skip to content

Using Stores in Components

Using a Pinia store in a component is straightforward: call the store composable and access state, getters, and actions directly on the returned object.

<script setup lang="ts">
import { useFamilyStore } from '../stores/familyStore'
const store = useFamilyStore()
</script>
<template>
<p>People: {{ store.people.length }}</p>
<p v-if="store.focusPerson">Focused: {{ store.focusPerson.name }}</p>
<button @click="store.setFocus('abc-123')">Set focus</button>
</template>

store.people is reactive — when it changes, the template re-renders. store.setFocus() calls the action.

Destructuring a Pinia store loses reactivity for state and getters:

// ❌ Breaks reactivity — people is now a plain array
const { people, focusPerson } = useFamilyStore()

storeToRefs fixes this — it wraps state and getters in refs, preserving reactivity:

import { storeToRefs } from 'pinia'
const store = useFamilyStore()
const { people, focusPerson, parents, children, currentSpouse } = storeToRefs(store)

Now people, focusPerson, etc. are reactive refs. Use them directly in the template (auto-unwrapped) or with .value in script.

Actions don’t need storeToRefs — they’re plain functions:

const { addPerson, deletePerson, setFocus } = store // ✅ actions are fine

FocusView.vue uses the store for all rendered data:

<script setup lang="ts">
import { useFamilyStore } from '../../stores/familyStore'
const store = useFamilyStore()
</script>
<template>
<PersonCard
v-for="parent in store.parents"
:key="parent.id"
:person="parent"
role="parent"
:clickable="true"
@select="store.setFocus"
/>
<PersonCard
:person="store.focusPerson"
role="focus"
/>
<PersonCard
v-if="store.currentSpouse"
:person="store.currentSpouse"
role="spouse"
:clickable="true"
@select="store.setFocus"
/>
</template>

PersonDetailView.vue uses storeToRefs to destructure the people ref, then computes locally:

const store = useFamilyStore()
const { people } = storeToRefs(store)
const person = computed(() => people.value.find(p => p.id === id))

FamilyTree uses a single store, but larger apps often have multiple stores — one for authentication, one for the cart, one for notifications. Each store is independent and can be used in any component.

  1. Import useFamilyStore in a component.
  2. Use storeToRefs to destructure people and focusPerson.
  3. Render focusPerson.name (with a v-if guard) and the count of people.
  4. Add a button that calls store.addPerson({ name: 'Test', ... }) and verify the count increases.
  • Call useStoreName() in <script setup> to get the store instance.
  • Access state, getters, and actions directly on the store object — all reactive.
  • Use storeToRefs(store) to destructure state and getters while preserving reactivity.
  • Destructure actions directly from the store — functions don’t lose reactivity.