Pinia: A Type-Safe Store for Vue 3 Without Vuex's Dynamic Module Problem
🍍 Intuitive, type safe, light and flexible Store for Vue using the composition api with DevTools support
At a glance
- What is it?
- Pinia is the Vue state management library that replaces Vuex. It installs as a plugin, defines stores per file, and drops dynamic modules because they cannot be typed. Here is how it works, where it stops, and what to check before migrating.
- Who is it for?
- Adopt Pinia on Vue 3 if you want per-file stores, getters and actions with devtools support, and you accept that dynamic modules are gone by design. Do not adopt it for a Vue 2 codebase: the README points Vue 2 users at the v2 branch, and the v4 line is built for Vue 3.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Vuex successor question, answered in the README
Pinia exists because Vuex's shape did not fit the Composition API. The README states the position bluntly: asked whether Pinia is the successor of Vuex, it answers yes and links to the Vue scaling-up guide. That is the maintainers' own framing, not a community claim.
The problem it solves is concrete. A Vuex store is a single tree with modules registered into it. Pinia instead treats each store as a unit you define in its own file, with state, getters and actions living together. The README describes the design as modular, and the pineapple metaphor in the README is about exactly this: individual flowers that join into one fruit, stores born individually but connected.
Who it is for: Vue 3 developers who want shared state with TypeScript types that survive refactoring. Who it is not for: Vue 2 projects. The README says the latest version of Pinia works with Vue 3 and directs Vue 2 users to the v2 branch. That is a hard boundary, not a preference.
How a Pinia store is defined and consumed
The mechanism is a factory. defineStore takes a unique string id and an options object, and returns a function you call to get the store instance. The id matters because it is the name that appears in devtools.
Inside the options object, state is a function returning a fresh object, getters receive state as their first parameter, and actions mutate through this. The README's example shows a getter calling another getter through this.doubleCounter, and an action resetting counter to zero. That is the whole data flow: state is the source, getters derive from it, actions write to it.
Consumption is where the design bites. Because the store is a reactive object, destructuring it directly loses reactivity. The README imports storeToRefs to pull individual state and getter properties out while keeping them reactive, and returns the whole store object separately for template access. This is the detail that trips up people arriving from plain reactive objects, and the README treats it as the normal pattern rather than an edge case.
Installing Pinia and wiring it into a Vue 3 app
The README gives one install command, with pnpm and yarn noted as alternatives in a comment. It installs two packages: pinia itself and @vue/devtools-api.
# or pnpm or yarn
npm install pinia @vue/devtools-apiAfter installing, you create the root store with createPinia and register it on the app instance before mounting. The README labels this block as the Vue 3 path.
// Vue 3
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')Then define a store in its own file. The README says you can create as many stores as you want and that they should each exist in different files.
import { defineStore } from 'pinia'
export const useMainStore = defineStore('main', {
state: () => ({
counter: 0,
name: 'Eduardo',
}),
getters: {
doubleCounter: (state) => state.counter * 2,
doubleCounterPlusOne(): number {
return this.doubleCounter + 1
},
},
actions: {
reset() {
this.counter = 0
},
},
})In a component, call the returned function and use storeToRefs to extract the properties you want to stay reactive. The README's setup example returns both the store object and the extracted refs.
import { useMainStore } from '@/stores/main'
import { storeToRefs } from 'pinia'
const main = useMainStore()
const { counter, doubleCounter } = storeToRefs(main)What you should see: the store id 'main' listed in Vue devtools, counter starting at 0, and doubleCounter tracking it. For Nuxt, the README does not inline the configuration. It points to the SSR/Nuxt page of the documentation and lists a @pinia/nuxt module as a feature. Treat that link as required reading rather than optional.
Dynamic modules are gone, and that is deliberate
The most consequential limitation is stated in the README's own FAQ. Asked about dynamic modules, the answer is that dynamic modules are not type safe, so Pinia instead allows creating different stores that can be imported anywhere.
Read that as a trade-off rather than a missing feature. If your Vuex codebase registers modules at runtime, for example per route or per tenant, Pinia has no equivalent registration API. You restructure into separate stores and import them where needed. That is a real migration cost, and the README does not offer a compatibility shim.
The second limitation is version scope. The v4 line targets Vue 3. Vue 2 users are sent to the v2 branch, which means fixes and features land in two places. The README does not describe how long v2 will be supported, and it does not document a rollback path for the plugin installation. If you need a rollback story, the README is silent on it.
A third point worth naming: the README shows the Options-style store definition, with state, getters and actions as keys. It does not present a setup-style alternative in the README, so anyone expecting the newer syntax should check the documentation rather than assume it from the README.
Pinia versus Vuex and Redux in practice
Vuex is the direct alternative and the one the README itself addresses. The difference in approach is structural: Vuex centers on one store with modules, Pinia centers on independently defined stores. Vuex's mutations are a separate concept from actions; Pinia's options object has state, getters and actions, with no mutation layer shown in the README example. That removes a step in the write path, though it also removes the mutation log some teams relied on for debugging.
Redux is the other comparison people search for, and the gap is wider. Redux is framework-agnostic and built around reducers and a single immutable state tree. Pinia is Vue-specific, its state is a reactive object you mutate directly in actions, and it ships as a Vue plugin registered with app.use. If you need to share state logic with a React or React Native app, Pinia is the wrong tool; it is bound to Vue's reactivity system.
For Nuxt users, the @pinia/nuxt module is the packaging difference. The README lists it as a feature and links to the Nuxt configuration page, but does not reproduce the setup inline. That is a documentation gap you should close before starting a Nuxt project.
Maintenance, licensing and upgrade cost
The repository is not archived, and the last push was on 2026-09-06, which is recent. Releases are active: v4.0.3 landed on 2026-08-12, alongside @pinia/[email protected] the same day, with @pinia/[email protected] on 2026-07-16. The versioning is worth noting: the core package and the Nuxt module version independently, so you cannot assume a matching number means a matching release.
The repository is a pnpm workspace. The root package.json declares workspaces under packages/*, and the build script builds packages/pinia, packages/nuxt and packages/testing in sequence. The test script is heavier than most: it runs dev:prepare, type tests via tsc --build, the vitest suite, a build, then test:dts and test:dist. If you fork or vendor Pinia, that is the pipeline you inherit.
Licensing is MIT, stated in the README and present as a LICENSE file at the repository root. MIT permits commercial use and modification with the licence and copyright notice retained. That is a description of the licence text, not legal advice; your own counsel decides what your distribution requires.
Upgrade cost from v2 to v4 is not documented in the README. The only migration signal available is the Vue 2 versus Vue 3 split, so budget time to read the release notes before moving a production app.
Editorial conclusion
Adopt Pinia on Vue 3 if you want per-file stores, getters and actions with devtools support, and you accept that dynamic modules are gone by design. Do not adopt it for a Vue 2 codebase: the README points Vue 2 users at the v2 branch, and the v4 line is built for Vue 3. Before committing, verify the exact install command and plugin wiring against the documentation at pinia.vuejs.org, and confirm the Nuxt setup separately, because the README only shows the Vue 3 createApp path in full.
Frequently asked questions
What is Pinia used for?
Pinia is a store for Vue, described in the README as intuitive, type safe and flexible. You use it to hold shared state, derive values with getters, and mutate that state through actions, with the stores visible in devtools.
What is a Store in Pinia?
A store is created by calling defineStore with a unique name and an options object containing state, getters and actions. defineStore returns a function that you call to get access to the store instance.
What is the difference between Vuex and Pinia?
The README states Pinia is the successor of Vuex. The structural difference is that Vuex uses one store with modules, while Pinia lets you create different stores that can be imported anywhere, and it drops dynamic modules because they are not type safe.
How do I install Pinia in a Vue 3 project?
Run npm install pinia @vue/devtools-api, then create the root store with createPinia and register it with app.use(pinia) before app.mount. The README labels that wiring as the Vue 3 path.
How do I use a Pinia store outside a component?
The README does not cover calling a store outside a component. It only shows the setup() pattern, where useMainStore() is called inside the component and storeToRefs extracts reactive properties.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vuejs-pinia)