Skip to the solution
MediumFrontend11 min read

What is Pinia and why use it?

379 views

The problem

Question presented to candidate: "Several unrelated parts of our Vue app need the same cart and user data, and props are getting passed five levels deep. What is Pinia, how does a store look, and why would you pick it over a plain reactive() object or Vuex?"

What a strong answer should cover:

  • Pinia is the official state management library for Vue (the Vuex README now says "Pinia is now the new default"); a store is a reactive object shared by every component that calls useXxxStore().
  • The three parts: state, getters (cached, like computed) and actions (methods, sync or async); both the option store and setup store syntaxes.
  • What it adds over a module-level reactive(): devtools integration, SSR safety (one store set per request), plugins, HMR, $patch / $subscribe / $onAction, and a conventional structure.
  • What it removed compared with Vuex: no mutations, no nested modules or namespaces, full TypeScript inference without wrappers.
  • Practical details: app.use(createPinia()), destructure with storeToRefs, $reset() exists only on option stores.

Clarifying questions expected:

  • "Is this app server-rendered, or client-only?"
  • "Is the shared data client state, or mostly server data that a query cache might handle better?"

Code / implementation expected: Yes β€” a small store (state, a getter, an action) and a component that uses it.

  • pinia
  • state

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

11 min read Β· 9 sections

Read the code

VueThe problem Pinia solves: shared state between unrelated components (no Pinia)
<script setup>
// The problem Pinia solves, without Pinia: two components that are not
// parent and child need the same cart. A module-level reactive() works in a
// small client-only app; Pinia adds devtools, SSR safety, plugins and a
// conventional shape (state, getters, actions) on top of the same idea.
import { reactive, computed, defineComponent, h } from "vue";

const cart = reactive({
  items: [],
  get count() { return this.items.reduce((n, i) => n + i.qty, 0); },
  add(name) {
    const line = this.items.find((i) => i.name === name);
    line ? line.qty++ : this.items.push({ name, qty: 1 });
  },
});

const ProductList = defineComponent({
  setup: () => () =>
    h("div", ["Apple", "Pear"].map((name) => h("button", { onClick: () => cart.add(name) }, `Add ${name}`))),
});

const HeaderBadge = defineComponent({
  setup() {
    const label = computed(() => `Cart (${cart.count})`);
    return () => h("strong", label.value);
  },
});
</script>

<template>
  <HeaderBadge />
  <ProductList />
  <ul>
    <li v-for="line in cart.items" :key="line.name">{{ line.name }} x {{ line.qty }}</li>
  </ul>
</template>
An option store and a setup store
import { defineStore } from "pinia";
import { ref, computed } from "vue";

// Option store: state / getters / actions, like a component options object
export const useCounterStore = defineStore("counter", {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment(by = 1) {
      this.count += by;
    },
  },
});

// Setup store: a setup() function. ref = state, computed = getter, function = action
export const useCartStore = defineStore("cart", () => {
  const items = ref<{ sku: string; price: number }[]>([]);
  const total = computed(() => items.value.reduce((sum, i) => sum + i.price, 0));
  function add(sku: string, price: number) {
    items.value.push({ sku, price });
  }
  return { items, total, add };
});

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 60 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextMedium

How do you optimize performance in a Vue app?

Continue