Vue State

Pinia

Pinia é a store oficial do Vue: simples, type-safe e construída sobre a Composition API. State, getters e actions em um só lugar, sem providers ou boilerplate.

intermediate12 min readUpdated 15 de set. de 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 };
});
Mantido por
A equipe do Vue
Construído sobre
A Composition API
Stores
defineStore
State
ref / reactive
Derivados
computed
Devtools
Suporte a Time-travel

Por que importa

Por que o Pinia substituiu o Vuex

Simples por design

Uma store é uma função que retorna state e actions. Não há mutations, módulos ou providers para aprender.

Type-safe

Pinia foi escrito com TypeScript em mente, então state e actions inferem seus tipos com quase nenhuma anotação extra.

Nativo da Composition API

Setup stores usam ref, computed e funções diretamente, então o código da store se parece com o restante dos seus componentes.

O panorama completo

As três partes de uma store Pinia

State, getters e actions. Em uma setup store, eles são apenas refs, computeds e funções.

State

Dados

Valores reativos declarados com ref ou reactive e retornados pela store.

Getters

Derivação

Valores computados derivados do state, cacheados e compartilhados entre componentes.

Actions

Atualizações

Funções que alteram o state, executam lógica assíncrona e podem chamar outras stores.

Pinia em resumo

O núcleo do Pinia

defineStore

Crie uma store com um id único e utilizeeither uma função de setup ou de options.

State

Valores reativos que guardam os dados compartilhados pelo seu app.

Getters

Valores computados derivados do state, cacheados automaticamente.

Actions

Funções que atualizam o state e podem ser assíncronas.

Plugins

Adicione persistência, logging ou outros comportamentos a cada store.

Stores em componentes

Chame o hook da store e acesse seu state diretamente, sem a necessidade de providers.

Uma breve historia

Do Vuex à store oficial

  1. 2019

    Pinia introduzido

    Uma store mais leve e amigável à Composition API surge como alternativa ao Vuex.

    19
  2. 2020

    Vuex 4

    Vuex é lançado para Vue 3, mas seu sistema de módulos e mutations parecem pesados.

    20
  3. 2022

    Oficial para Vue 3

    Pinia torna-se a store oficialmente recomendada e é incluída em novos projetos Vue.

    22
  4. 2023

    Integração com Nuxt

    Nuxt 3 lança suporte ao Pinia e auto-importação de stores.

    23
  5. Hoje

    O padrão do Vue

    Pinia é a solução de state padrão para aplicações Vue e Nuxt.

    Hoje

O guia completo

Pinia: Tudo que voce precisa saber

O que é Pinia?

Pinia é a biblioteca oficial de gerenciamento de estado para Vue. Ela substituiu o Vuex como a store recomendada no Vue 3 e agora é o padrão em novos projetos Vue e Nuxt. É leve, type-safe e construída diretamente sobre a Composition API.

O design é intencionalmente minimalista. Uma store é uma função que retorna state, valores derivados e actions. Não existem mutations, nem namespacing de módulos ou providers por store. Se você conhece ref, computed e funções, você já conhece a maior parte do Pinia.

Definindo uma store

Uma store é criada com defineStore, que recebe um id único e uma função de 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 };
});

Esta é uma setup store: ref torna-se o state, computed torna-se um getter e as funções tornam-se actions. O id único nomeia a store no devtools e é obrigatório.

O Pinia também suporta uma options store, caso você prefira a estrutura semelhante ao Vuex:

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

Ambas as formas são totalmente suportadas. Setup stores tendem a se encaixar melhor com TypeScript e a Composition API, enquanto options stores podem parecer mais familiares para usuários de Vuex.

Usando uma store em componentes

Chame a função da store para obter a instância da store. Não há necessidade de um provider para envolver seus componentes.

<!-- 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>

A leitura de cart.total diretamente em um template permanece reativa. Quando você quiser fazer o destructuring do estado ou dos getters mantendo a reatividade, use storeToRefs, que os converte em refs. As actions podem sofrer destructuring diretamente, pois não precisam de reatividade.

Getters

Getters são valores computados derivados do estado. Eles são cacheados e compartilhados, portanto, todo componente que lê o mesmo getter compartilha o mesmo cálculo.

// 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 };
});

Tentar usar watch para computar um valor é um erro comum. Se o valor puder ser derivado do estado existente, ele deve ser um getter, exatamente da mesma forma que você usaria computed em um componente.

Actions

Actions são funções que alteram o estado. Elas podem ser síncronas ou assíncronas, e podem chamar outras actions ou até mesmo outras 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 };
});

Para dados do servidor, no entanto, considere uma camada de busca de dados como o TanStack Query, que gerencia cache e invalidação. O Pinia é ideal para estado do cliente: carrinhos, filtros, preferências de UI, estado de autenticação e rascunhos.

Plugins e persistência

Os plugins do Pinia são executados para cada store e podem adicionar comportamentos como persistência, logging ou reset.

// 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);

O popular pinia-plugin-persistedstate faz a mesma coisa, oferecendo opções sobre quais chaves devem ser armazenadas. A persistência é um dos principais motivos para se utilizar uma store em vez do estado local de um componente.

Pinia com Nuxt

No Nuxt, instale o módulo oficial e as stores em stores/ serão importadas automaticamente.

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

Como o Nuxt gerencia a instalação e a renderização no servidor, a mesma store funciona tanto no servidor quanto no cliente. Consulte o guia do Nuxt para entender como isso se integra ao useFetch e às rotas de servidor.

Melhores práticas

  • Prefira setup stores para códigos novos, especialmente com TypeScript.
  • Mantenha dados derivados em getters em vez de observar o estado.
  • Use storeToRefs ao desestruturar o estado ou getters.
  • Mantenha dados do servidor em uma camada de data-fetching, não no Pinia.
  • Use actions para qualquer coisa que altere o estado, incluindo operações assíncronas.
  • Persista apenas o estado que deve sobreviver a um reload.
  • Nomeie as stores de forma clara, com base no domínio que elas gerenciam.

Erros comuns

  • Desestruturar o state sem storeToRefs e perder a reatividade.
  • Usar watch para calcular um valor que deveria ser derivado por um getter.
  • Colocar dados buscados do servidor no store e gerenciar manualmente as flags de carregamento.
  • Esquecer de instalar o Pinia, fazendo com que as chamadas do store falhem em runtime.
  • Criar um único store enorme em vez de stores focados em domínios específicos.
  • Mutar o state fora de uma action de uma forma que seja difícil de rastrear.

Próximos passos

O Pinia é o equivalente do Vue para um store pequeno e moderno. Aprofunde seus conhecimentos em Vue, adicione Nuxt para renderização no servidor e auto-imports, e compare essa abordagem com a Context API, Zustand e Redux Toolkit do React. Depois, crie um store pequeno com um getter, uma action assíncrona e persistência para ver a pouca quantidade de código necessária.

Definindo uma store

Uma setup store usa a mesma sintaxe de ref, computed e funções que seus componentes, portanto há menos para aprender.

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++; },
    },
  },
);

Valores derivados

Use getters (computed) para valores derivados do state. Não use watch no state apenas para armazenar um valor calculado.

Prefira
const total = computed(() =>
  items.value.reduce(
    (sum, i) => sum + i.price, 0,
  ),
);
Evite
const total = ref(0);
watch(items, () => {
  total.value = items.value.reduce(
    (sum, i) => sum + i.price, 0,
  );
});

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Pinia?

Nosso tutorial interativo te guia por Pinia passo a passo — com quizzes e codigo real que voce pode executar no navegador.