Pinia in Nuxt
Pinia is the official store library for Vue; you know it from the Vue course. In Nuxt it is connected through the @pinia/nuxt module, which sets Pinia up for server rendering and passes store state to the browser. Pinia pays off when shared state grows and actions and computed values appear next to the data.
Installing
npx nuxi module add piniaThe command installs the pinia and @pinia/nuxt packages and adds the module to the config:
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
modules: ['@pinia/nuxt']
})After restarting npm run dev, the functions defineStore and storeToRefs are available without an import.
A store
Stores go into the app/stores/ folder. Create app/stores/favorites.ts, the reader's favourite posts:
// app/stores/favorites.ts
export const useFavoritesStore = defineStore('favorites', () => {
const ids = ref<number[]>([])
const count = computed(() => ids.value.length)
function toggle(id: number) {
if (ids.value.includes(id)) {
ids.value = ids.value.filter((item) => item !== id)
} else {
ids.value.push(id)
}
}
return { ids, count, toggle }
})This is a setup-style store: a ref becomes state, a computed becomes a getter, and a function becomes an action.
Using it
Add a button to the card app/components/PostCard.vue:
<!-- app/components/PostCard.vue -->
<script setup lang="ts">
defineProps<{
post: { id: number; title: string; text: string }
}>()
const favorites = useFavoritesStore()
</script>
<template>
<article class="post-card">
<h3>{{ post.title }}</h3>
<button @click="favorites.toggle(post.id)">
{{ favorites.ids.includes(post.id) ? 'Remove from favourites' : 'Add to favourites' }}
</button>
</article>
</template>And a counter in the layout header:
<!-- app/layouts/default.vue -->
<script setup lang="ts">
const favorites = useFavoritesStore()
</script>
<template>
<header>
<NuxtLink to="/posts">Posts</NuxtLink>
<span>Favourites: {{ favorites.count }}</span>
</header>
</template>Mark two posts on /posts and the header shows "Favourites: 2". The module imports stores from app/stores/ automatically; if that does not work in your module version, import explicitly: import { useFavoritesStore } from '~/stores/favorites'.
useState or Pinia
| Task | What to choose |
|---|---|
| One or two simple values | useState in a composable |
| Data together with actions and computed values | Pinia |
| You want to watch changes in DevTools | Pinia |
Common mistake
Destructuring a store breaks reactivity: after const { count } = useFavoritesStore() the variable count stays at its initial value forever. Access fields through the object (favorites.count) or use storeToRefs: const { count } = storeToRefs(favorites). Actions can be taken out directly: const { toggle } = favorites.