Frontend Development 10 min read

Is Pinia/Vuex Really Necessary? A Critical Look at State Management in Vue 3

This article examines whether Pinia or Vuex is required for Vue 3 projects by comparing their features, discussing scenarios such as shared state, devtools, hot‑module replacement, TypeScript support, SSR, and offering simple native alternatives with code examples.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Is Pinia/Vuex Really Necessary? A Critical Look at State Management in Vue 3

When talking about Pinia , developers familiar with Vue 3 immediately recognize it as the officially recommended replacement for vuex , becoming part of the Vue ecosystem.

The author recalls the earlier Vuex workflow where state updates could be synchronous or asynchronous, a topic often raised in interviews, and notes that Pinia allows both without extra ceremony.

Question: Do we really need Pinia/Vuex ? What problems do they solve, and how did we handle state before they existed?

The author shares personal reflections on why Pinia might be considered necessary.

1. Pinia is a lightweight state‑management library for Vue.js

The Vue documentation describes a scenario where multiple components share a common state, recommending a global store. A simple native solution is shown:

// store.js
import { reactive } from "vue";

export const store = reactive({
  count: 0,
  // ...
});

This simple approach solves the immediate state‑sharing problem.

2. Devtools support

The author admits never using Vue Devtools, relying on console.log instead, and therefore skips this point.

3. Hot‑module replacement (HMR)

While Pinia mentions hot‑updating stores without a full page reload, the author argues that the same effect can be achieved with standard bundler HMR (e.g., Vite or Webpack) and therefore does not explore it further.

4. Plugin extensibility

Plugin extensions are noted but left for future consideration.

5. TypeScript support and auto‑completion

TypeScript declarations provide editor auto‑completion, which can also be achieved with native Vue stores:

// store.ts
import { reactive } from "vue";

interface IStore {
  count: number;
  // ...
}
export const store = reactive
({
  count: 0,
  // ...
});

Auto‑completion is an editor feature, so the author skips further discussion.

6. Server‑Side Rendering (SSR) support

Using the simple store directly can cause cross‑request state pollution. The author proposes re‑initialising the store per request:

// store.js
import { reactive } from "vue";

export let store = createStore();
export const createStore = () => {
  return reactive({
    count: 0,
    // ...
  });
};
// app.js (shared between server and client)
import { createSSRApp } from 'vue';
import { createStore, store } from './store.js';

export function createApp() {
  // Re‑initialise store for each request
  store = createStore();
  const app = createSSRApp(/* ... */);
  return { app };
}

The official Pinia SSR guide is referenced, but the author believes the native approach is equally viable without extra learning cost.

Conclusion

After analysis, the author finds no compelling reason to mandate Pinia; while points 1 and 6 may be useful in some cases, the same outcomes can be achieved with simple native stores, avoiding unnecessary conceptual overhead.

Only state management needed

// store.js
import { reactive } from "vue";
export const store = reactive({
  count: 0,
  // ...
});

When SSR is required

// store.js (same as above with factory function)
import { reactive } from "vue";
export let store = createStore();
export const createStore = () => reactive({ count: 0 /* ... */ });
// app.js (re‑initialise per request)
import { createSSRApp } from 'vue';
import { createStore, store } from './store.js';
export function createApp() {
  store = createStore();
  const app = createSSRApp(/* ... */);
  return { app };
}

Other considerations

Some argue that the simple approach may become hard to maintain as data grows. The author counters that modularising the store (e.g., splitting into moduleAStore , moduleBStore ) mitigates this issue.

Regarding page refresh losing store data, the author suggests persisting only the necessary parts (e.g., login info) using localStorage or similar, rather than persisting the entire store.

// Persist login info on unload
import { store } from './store.js';
window.onbeforeunload = () => {
  localStorage.setItem("__loginInfo__", JSON.stringify(store.loginInfo));
};
// Restore login info on load
import { store } from './store.js';
const loginInfo = localStorage.getItem("__loginInfo__");
if (loginInfo) {
  store.loginInfo = JSON.parse(loginInfo);
}

The author emphasizes that caching should be scenario‑driven, not a blanket store‑wide solution.

Final thoughts

纸上得来终觉浅,绝知此事要躬行

Learning stagnates when concepts and tools proliferate excessively; the author advocates using native knowledge whenever possible to keep the mental load low and stay curious about the problem’s essence.

Keep it simple, stupid (KISS)
frontendtypescriptstate managementSSRVuePiniaVuex
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.