pinia
🍍 Intuitive, type safe, light and flexible Store for Vue using the composition api with DevTools support
Top Related Projects
Quick Overview
Pinia is the official state management library for Vue.js applications. It provides a simple and intuitive API for managing global state, with full TypeScript support and devtools integration. Pinia is designed to be lightweight and flexible, making it suitable for projects of all sizes.
Pros
- Excellent TypeScript support with autocompletion
- Modular design allows for better code organization and scalability
- Seamless integration with Vue 3 Composition API
- Built-in devtools support for easy debugging and state inspection
Cons
- Learning curve for developers new to state management concepts
- Limited ecosystem compared to older solutions like Vuex
- May be overkill for small projects with simple state requirements
Code Examples
- Creating a store:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
},
},
})
- Using the store in a component:
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>Count: {{ counter.count }}</div>
<button @click="counter.increment()">Increment</button>
</template>
- Composing stores:
import { defineStore } from 'pinia'
import { useUserStore } from './user'
export const useCartStore = defineStore('cart', {
state: () => ({ items: [] }),
getters: {
total() {
return this.items.reduce((sum, item) => sum + item.price, 0)
},
},
actions: {
async checkout() {
const userStore = useUserStore()
await api.checkout(this.items, userStore.token)
this.items = []
},
},
})
Getting Started
- Install Pinia:
npm install pinia
- Create a Pinia instance in your main.js:
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')
- Create a store:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
},
},
})
- Use the store in a component:
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>Count: {{ counter.count }}</div>
<button @click="counter.increment()">Increment</button>
</template>
Competitor Comparisons
A JS library for predictable global state management
Pros of Redux
- Widely adopted and battle-tested in large-scale applications
- Extensive ecosystem with many middleware options and developer tools
- Framework-agnostic, can be used with any UI library or framework
Cons of Redux
- Steeper learning curve due to concepts like reducers, actions, and middleware
- More boilerplate code required for setting up stores and actions
- Can be overkill for smaller applications or simpler state management needs
Code Comparison
Redux:
const store = createStore(reducer);
const action = { type: 'INCREMENT' };
store.dispatch(action);
const state = store.getState();
Pinia:
const store = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++ }
}
});
store.increment();
const count = store.count;
Redux requires more setup code and explicit action dispatching, while Pinia offers a more concise and intuitive API. Pinia's store definition is more straightforward, with state and actions defined in a single object. Redux, on the other hand, separates concerns into reducers, actions, and store creation, which can lead to more verbose code but also provides more flexibility for complex state management scenarios.
Simple, scalable state management.
Pros of MobX
- Framework-agnostic, can be used with React, Vue, or vanilla JavaScript
- Automatic tracking of observables, reducing boilerplate code
- Supports computed values and reactions out of the box
Cons of MobX
- Steeper learning curve due to its more complex API
- Less predictable state updates, as mutations can happen anywhere
- Potential for overuse of observables, leading to performance issues
Code Comparison
MobX:
import { makeObservable, observable, action } from "mobx";
class Store {
count = 0;
constructor() {
makeObservable(this, {
count: observable,
increment: action
});
}
increment() {
this.count++;
}
}
Pinia:
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++;
}
}
});
Both MobX and Pinia offer state management solutions, but they differ in their approach and ecosystem integration. MobX provides a more flexible, framework-agnostic solution with powerful reactive programming concepts, while Pinia offers a simpler, Vue-specific state management experience with better TypeScript support and Vue devtools integration.
🐻 Bear necessities for state management in React
Pros of Zustand
- Framework-agnostic, can be used with any UI library or vanilla JavaScript
- Simpler API with less boilerplate code
- Supports middleware and devtools out of the box
Cons of Zustand
- Less opinionated structure, which may lead to inconsistent patterns in larger projects
- Lacks built-in TypeScript support for action typing (though can be added manually)
- No built-in support for modules or namespacing
Code Comparison
Zustand:
import create from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
Pinia:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
},
},
})
Both Zustand and Pinia are popular state management libraries, but they cater to different needs. Zustand offers simplicity and flexibility, making it a great choice for small to medium-sized projects or when working outside the Vue ecosystem. Pinia, being tailored for Vue.js, provides a more structured approach with better TypeScript integration, making it ideal for large-scale Vue applications.
The Redux Framework
Pros of Rematch
- Framework-agnostic, works with React, Vue, and other libraries
- Built-in support for async actions and effects
- More flexible model structure with less boilerplate
Cons of Rematch
- Smaller community and ecosystem compared to Pinia
- Less Vue-specific optimizations and integrations
- Steeper learning curve for Vue developers familiar with Vuex/Pinia patterns
Code Comparison
Rematch:
const count = {
state: 0,
reducers: {
increment(state, payload) {
return state + payload
}
},
effects: (dispatch) => ({
async incrementAsync(payload) {
await new Promise(resolve => setTimeout(resolve, 1000))
dispatch.count.increment(payload)
}
})
}
Pinia:
export const useCountStore = defineStore('count', {
state: () => ({ count: 0 }),
actions: {
increment(amount) {
this.count += amount
},
async incrementAsync(amount) {
await new Promise(resolve => setTimeout(resolve, 1000))
this.increment(amount)
}
}
})
Both Rematch and Pinia offer state management solutions, but Rematch is more flexible and framework-agnostic, while Pinia is tailored for Vue.js applications. Rematch provides a more Redux-like approach with separate reducers and effects, whereas Pinia follows a simpler, more Vue-oriented structure with combined state and actions.
Business logic with ease ☄️
Pros of Effector
- Framework-agnostic, can be used with any UI library or framework
- Offers more fine-grained control over state updates and side effects
- Provides powerful tools for handling asynchronous operations and complex data flows
Cons of Effector
- Steeper learning curve due to its unique approach and concepts
- Less integrated with Vue.js ecosystem compared to Pinia
- Smaller community and fewer ready-made plugins/extensions
Code Comparison
Effector:
import {createStore, createEvent} from 'effector'
const increment = createEvent()
const $counter = createStore(0)
.on(increment, state => state + 1)
Pinia:
import {defineStore} from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
}
}
})
Both Effector and Pinia are state management solutions, but they differ in their approach and target audience. Effector is more flexible and powerful, suitable for complex applications across different frameworks. Pinia, on the other hand, is specifically designed for Vue.js applications, offering a simpler API and better integration with the Vue ecosystem. The choice between them depends on the project requirements, team expertise, and the specific framework being used.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Pinia
Intuitive, type safe and flexible Store for Vue
- ð¡ Intuitive
- ð Type Safe
- âï¸ Devtools support
- ð Extensible
- ð Modular by design
- ð¦ Extremely light
- â°ï¸ Nuxt Module
Pinia works with both Vue 2 and Vue 3.
Pinia is the most similar English pronunciation of the word pineapple in Spanish: piña. A pineapple is in reality a group of individual flowers that join together to create a multiple fruit. Similar to stores, each one is born individually, but they are all connected at the end. It's also a delicious tropical fruit indigenous to South America.
ð Demo with Vue 3 on StackBlitz
ð Demo with Nuxt 3 on StackBlitz
Help me keep working on this project ð
Silver Sponsors
Bronze Sponsors
FAQ
A few notes about the project and possible questions:
Q: Is Pinia the successor of Vuex?
A: Yes
Q: What about dynamic modules?
A: Dynamic modules are not type safe, so instead we allow creating different stores that can be imported anywhere
Installation
# or pnpm or yarn
npm install pinia
If you are using Vue <2.7, make sure to install latest @vue/composition-api
:
npm install pinia @vue/composition-api
Usage
Install the plugin
Create a pinia (the root store) and pass it to app:
// 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')
// Vue 2
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// other options...
// ...
// note the same `pinia` instance can be used across multiple Vue apps on
// the same page
pinia,
})
For more detailed instructions, including Nuxt configuration, check the Documentation.
Create a Store
You can create as many stores as you want, and they should each exist in different files:
import { defineStore } from 'pinia'
// main is the name of the store. It is unique across your application
// and will appear in devtools
export const useMainStore = defineStore('main', {
// a function that returns a fresh state
state: () => ({
counter: 0,
name: 'Eduardo',
}),
// optional getters
getters: {
// getters receive the state as first parameter
doubleCounter: (state) => state.counter * 2,
// use getters in other getters
doubleCounterPlusOne(): number {
return this.doubleCounter + 1
},
},
// optional actions
actions: {
reset() {
// `this` is the store instance
this.counter = 0
},
},
})
defineStore
returns a function that has to be called to get access to the store:
import { useMainStore } from '@/stores/main'
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const main = useMainStore()
// extract specific store properties
const { counter, doubleCounter } = storeToRefs(main)
return {
// gives access to the whole store in the template
main,
// gives access only to specific state or getter
counter,
doubleCounter,
}
},
})
Documentation
To learn more about Pinia, check its documentation.
License
Top Related Projects
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot