Vue State

Pinia

Pinia est le store officiel de Vue : simple, type-safe et basé sur la Composition API. État, getters et actions au même endroit, sans providers ni boilerplate.

intermediate12 min readUpdated 15 sept. 2026
cart.js
js
// stores/cart.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCartStore = defineStore("cart", () => {
  const items = ref([]);

  const total = computed(() =>
    items.value.reduce((sum, item) => sum + item.price, 0),
  );

  function add(item) {
    items.value.push(item);
  }

  return { items, total, add };
});
Maintenu par
L'équipe Vue
Basé sur
La Composition API
Stores
defineStore
État
ref / reactive
Dérivé
computed
Devtools
Support du Time-travel

Pourquoi c'est important

Pourquoi Pinia a remplacé Vuex

Simple par conception

Un store est une fonction qui retourne un état et des actions. Il n'y a pas de mutations, de modules ou de providers à apprendre.

Type-safe

Pinia est écrit avec TypeScript en tête, donc l'état et les actions infèrent leurs types avec presque aucune annotation supplémentaire.

Natif Composition API

Les setup stores utilisent directement ref, computed et des fonctions, ainsi le code du store ressemble au reste de vos composants.

Le tableau complet

Les trois piliers d'un store Pinia

État, getters et actions. Dans un setup store, ce ne sont que des refs, des computeds et des fonctions.

État

Données

Valeurs réactives déclarées avec ref ou reactive et retournées par le store.

Getters

Dérivation

Valeurs calculées dérivées de l'état, mises en cache et partagées entre les composants.

Actions

Mises à jour

Fonctions qui modifient l'état, exécutent une logique asynchrone et peuvent appeler d'autres stores.

Pinia en un coup d'œil

Le cœur de Pinia

defineStore

Crée un store avec un id unique et soit une fonction setup, soit une fonction options.

État

Valeurs réactives qui détiennent les données partagées par votre application.

Getters

Valeurs calculées dérivées de l'état, mises en cache automatiquement.

Actions

Fonctions qui mettent à jour l'état et peuvent être asynchrones.

Plugins

Ajoutez de la persistance, du logging ou d'autres comportements à chaque store.

Stores dans les composants

Appelez le hook du store et accédez à son état directement, sans providers.

Un bref aperçu

De Vuex au store officiel

  1. 2019

    Introduction de Pinia

    Un store plus léger et compatible avec la Composition API apparaît comme alternative à Vuex.

    19
  2. 2020

    Vuex 4

    Vuex sort pour Vue 3, mais son système de modules et ses mutations semblent lourds.

    20
  3. 2022

    Officiel pour Vue 3

    Pinia devient le store officiellement recommandé et est inclus dans les nouveaux projets Vue.

    22
  4. 2023

    Intégration Nuxt

    Nuxt 3 intègre le support de Pinia et l'auto-import des stores.

    23
  5. Aujourd'hui

    Le standard Vue

    Pinia est la solution de gestion d'état standard pour les applications Vue et Nuxt.

    Aujourd'hui

Le guide complet

Pinia: Tout ce que vous devez savoir

Qu’est-ce que Pinia ?

Pinia est la bibliothèque officielle de gestion d’état pour Vue. Elle a remplacé Vuex en tant que store recommandé pour Vue 3 et est désormais le choix par défaut dans les nouveaux projets Vue et Nuxt. Elle est légère, type-safe et s’appuie directement sur la Composition API.

Sa conception est volontairement minimaliste. Un store est une fonction qui retourne un état, des valeurs dérivées et des actions. Il n’y a pas de mutations, pas de namespacing de modules, ni de provider par store. Si vous connaissez ref, computed et les fonctions, vous connaissez déjà l’essentiel de Pinia.

Définir un store

Un store est créé avec defineStore, qui prend un identifiant unique et une fonction de configuration (setup).

// stores/cart.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCartStore = defineStore("cart", () => {
  const items = ref([]);

  const total = computed(() =>
    items.value.reduce((sum, item) => sum + item.price, 0),
  );

  const count = computed(() => items.value.length);

  function add(item) {
    items.value.push(item);
  }

  function remove(id) {
    items.value = items.value.filter((item) => item.id !== id);
  }

  return { items, total, count, add, remove };
});

Il s’agit d’un setup store : ref devient l’état (state), computed devient un getter, et les fonctions deviennent des actions. L’identifiant unique permet de nommer le store dans les devtools et est obligatoire.

Pinia supporte également les options stores si vous préférez une structure similaire à Vuex :

// stores/counter.js
export const useCounter = defineStore("counter", {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count += 1;
    },
  },
});

Les deux formes sont entièrement supportées. Les setup stores ont tendance à mieux s’intégrer avec TypeScript et la Composition API, tandis que les options stores peuvent sembler plus familiers pour les utilisateurs de Vuex.

Utiliser un store dans les composants

Appelez la fonction du store pour obtenir l’instance du store. Il n’y a pas de provider à ajouter autour de vos composants.

<!-- Cart.vue -->
<script setup>
import { storeToRefs } from "pinia";
import { useCartStore } from "@/stores/cart";

const cart = useCartStore();
const { items, total } = storeToRefs(cart);
</script>

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }} — {{ item.price }}
      <button @click="cart.remove(item.id)">Remove</button>
    </li>
  </ul>
  <p>Total: {{ total }}</p>
</template>

La lecture de cart.total directement dans un template reste réactive. Lorsque vous souhaitez destructurer l’état ou les getters tout en conservant la réactivité, utilisez storeToRefs, qui les convertit en refs. Les actions peuvent être destructurées directement car elles n’ont pas besoin de réactivité.

Getters

Les getters sont des valeurs calculées dérivées de l’état. Ils sont mis en cache et partagés, ainsi chaque composant lisant le même getter partage le même calcul.

// stores/products.js
export const useProductsStore = defineStore("products", () => {
  const products = ref([]);
  const filter = ref("");

  const visible = computed(() =>
    products.value.filter((p) =>
      p.name.toLowerCase().includes(filter.value.toLowerCase()),
    ),
  );

  const inStock = computed(() =>
    visible.value.filter((p) => p.stock > 0),
  );

  return { products, filter, visible, inStock };
});

Tenter d’utiliser watch pour calculer une valeur est une erreur courante. Si une valeur peut être dérivée de l’état existant, elle doit être un getter, exactement comme vous utiliseriez computed dans un composant.

Actions

Les actions sont des fonctions qui modifient l’état. Elles peuvent être synchrones ou asynchrones, et peuvent appeler d’autres actions ou même d’autres stores.

// stores/users.js
export const useUsersStore = 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("/api/users");
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      users.value = await res.json();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  }

  return { users, loading, error, fetchUsers };
});

Cependant, pour les données provenant du serveur, envisagez une couche de récupération de données telle que TanStack Query, qui gère la mise en cache et l’invalidation. Pinia est idéal pour l’état client : paniers, filtres, préférences d’interface utilisateur, état d’authentification et brouillons.

Plugins et persistance

Les plugins Pinia s’exécutent pour chaque store et peuvent ajouter des comportements tels que la persistance, le logging ou la réinitialisation.

// persist.js
export function persistPlugin({ store }) {
  const saved = localStorage.getItem(store.$id);
  if (saved) store.$patch(JSON.parse(saved));

  store.$subscribe((_mutation, state) => {
    localStorage.setItem(store.$id, JSON.stringify(state));
  });
}

// main.js
const pinia = createPinia();
pinia.use(persistPlugin);
app.use(pinia);

Le populaire pinia-plugin-persistedstate fait la même chose, avec des options pour choisir les clés à stocker. La persistance est l’une des raisons principales pour lesquelles on privilégie un store plutôt que l’état local d’un composant.

Pinia avec Nuxt

Dans Nuxt, installez le module officiel et les stores situés dans stores/ seront auto-importés.

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@pinia/nuxt"],
});
<!-- pages/cart.vue -->
<script setup>
const cart = useCartStore(); // auto-imported
</script>

Comme Nuxt gère l’installation et le rendu serveur, le même store fonctionne aussi bien sur le serveur que sur le client. Consultez le guide Nuxt pour comprendre comment cela s’intègre avec useFetch et les routes serveur.

Bonnes pratiques

  • Privilégiez les setup stores pour le nouveau code, surtout avec TypeScript.
  • Gardez les données dérivées dans des getters plutôt que de surveiller l’état (state).
  • Utilisez storeToRefs lors de la déstructuration du state ou des getters.
  • Conservez les données serveur dans une couche de récupération de données (data-fetching), et non dans Pinia.
  • Utilisez des actions pour tout ce qui modifie l’état, y compris les opérations asynchrones.
  • Ne persistez que l’état qui doit survivre à un rechargement de la page.
  • Nommez vos stores clairement en fonction du domaine qu’ils gèrent.

Erreurs courantes

  • Déstructurer le state sans storeToRefs, ce qui entraîne une perte de réactivité.
  • Utiliser watch pour calculer une valeur qui devrait être dérivée via un getter.
  • Stocker des données récupérées depuis le serveur dans le store et gérer manuellement les indicateurs de chargement.
  • Oublier d’installer Pinia, provoquant l’échec des appels au store au moment de l’exécution.
  • Créer un seul store énorme au lieu de stores dédiés à des domaines précis.
  • Muter le state en dehors d’une action, rendant le traçage des modifications difficile.

Et après ?

Pinia est l’équivalent Vue d’un store moderne et léger. Approfondissez vos connaissances en Vue, ajoutez Nuxt pour le rendu serveur et les auto-imports, et comparez cette approche avec la Context API, Zustand et Redux Toolkit de React. Ensuite, créez un petit store avec un getter, une action asynchrone et de la persistance pour constater à quel point cela demande peu de code.

Définir un store

Un setup store utilise la même syntaxe ref, computed et fonction que vos composants, il y a donc moins de choses à apprendre.

Setup store
export const useCounter = defineStore(
  "counter",
  () => {
    const count = ref(0);
    const double = computed(
      () => count.value * 2,
    );
    const inc = () => count.value++;
    return { count, double, inc };
  },
);
Options store
export const useCounter = defineStore(
  "counter",
  {
    state: () => ({ count: 0 }),
    getters: {
      double: (s) => s.count * 2,
    },
    actions: {
      inc() { this.count++; },
    },
  },
);

Valeurs dérivées

Utilisez des getters (computed) pour les valeurs dérivées de l'état. Ne surveillez pas l'état simplement pour stocker une valeur calculée.

Préférer
const total = computed(() =>
  items.value.reduce(
    (sum, i) => sum + i.price, 0,
  ),
);
Éviter
const total = ref(0);
watch(items, () => {
  total.value = items.value.reduce(
    (sum, i) => sum + i.price, 0,
  );
});

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre Pinia ?

Notre tutoriel interactif vous guide à travers Pinia pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.