Vue 3 Pinia State Management
Implement centralized state management with Pinia for Vue 3 applications.
Overview
Pinia has become the official state management solution for Vue 3, replacing Vuex with a simpler, type-safe architecture that better leverages Vue 3's Composition API. Unlike Vuex's strict action-mutation-commit pattern, Pinia allows direct state mutations in actions while maintaining reactivity, reducing boilerplate significantly. The setup store syntax in Pinia mirrors the Composition API pattern, allowing colocated state, getters, and actions in a single function. This approach provides excellent TypeScript inference without decorators or complex type gymnastics. The returned object from setup() automatically becomes reactive, and functions can be passed as store actions. Getters in Pinia are computed properties that automatically track their dependencies. They can access other getters using this and can perform complex transformations on state. Unlike Vuex mutations which required separate getter objects, Pinia getters are defined alongside state, improving code organization for complex stores. Actions in Pinia are asynchronous by default, handling API calls and other async operations naturally. They can modify state directly, call other stores, and return values that callers can await. This flexibility, combined with Pinia's devtools integration for time-travel debugging and action history, makes debugging significantly easier than with Vuex.
Code Example
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import type { User, Project } from '@/types';
import { userApi } from '@/api/users';
import { useProjectStore } from './projects';
export const useUserStore = defineStore('user', () => {
// State
const currentUser = ref<User | null>(null);
const userProjects = ref<Project[]>([]);
const isLoading = ref(false);
const error = ref<string | null>(null);
// Getters
const isAuthenticated = computed(() => !!currentUser.value);
const userInitials = computed(() => {
if (!currentUser.value) return '';
return currentUser.value.name
.split(' ')
.map(n => n[0])
.join('')
.toUpperCase();
});
const recentProjects = computed(() => {
return userProjects.value
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
.slice(0, 5);
});
// Actions
async function login(email: string, password: string) {
isLoading.value = true;
error.value = null;
try {
const { user, token } = await userApi.login({ email, password });
currentUser.value = user;
localStorage.setItem('auth_token', token);
return user;
} catch (e) {
error.value = e instanceof Error ? e.message : 'Login failed';
throw e;
} finally {
isLoading.value = false;
}
}
async function fetchUserProjects() {
if (!currentUser.value) return;
isLoading.value = true;
try {
userProjects.value = await userApi.getProjects(currentUser.value.id);
} finally {
isLoading.value = false;
}
}
function logout() {
currentUser.value = null;
userProjects.value = [];
localStorage.removeItem('auth_token');
}
function updateProfile(updates: Partial<User>) {
if (!currentUser.value) return;
currentUser.value = { ...currentUser.value, ...updates };
}
return {
// State
currentUser,
userProjects,
isLoading,
error,
// Getters
isAuthenticated,
userInitials,
recentProjects,
// Actions
login,
fetchUserProjects,
logout,
updateProfile,
};
});
// Store with persistence
export const useSettingsStore = defineStore('settings', () => {
const theme = ref<'light' | 'dark'>('dark');
const sidebarCollapsed = ref(false);
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light';
}
return { theme, sidebarCollapsed, toggleTheme };
});More Vue.js Rules
Vue 3 Composition API Patterns
Prefer Composition API with script setup for better TypeScript support and reusable logic patterns.
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import { useAuth } from '@/composables/useAuth';
import { useTodos } f...Vue 3 Routing with Vue Router 4
Implement navigation guards, lazy loading, and advanced routing patterns in Vue 3.
<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router';
import { computed, onMounted } from 'vue';
import { useAuthStore } from '@/...Vue 3 Composables Pattern
Extract and reuse stateful logic with Vue 3 Composables using the Composition API.
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { useLocalStorage } from './useLocalStorage';
import { useFetch } from './u...