Pinia state management for Vue 3 including store creation, actions, getters, plugins, and DevTools integration.
Creates and manages Pinia stores for Vue 3 applications with type-safe state, actions, getters, and plugins.
npx claudepluginhub a5c-ai/babysitterThis skill is limited to using the following tools:
README.mdExpert assistance for implementing Pinia state management in Vue 3 applications.
Invoke this skill when you need to:
| Parameter | Type | Required | Description |
|---|---|---|---|
| storeName | string | Yes | Store name (use prefix) |
| stateShape | object | Yes | Initial state structure |
| actions | array | Yes | Store actions |
| getters | array | No | Computed getters |
| persist | boolean | No | Enable persistence |
{
"storeName": "useUserStore",
"stateShape": {
"user": null,
"isAuthenticated": false
},
"actions": ["login", "logout", "fetchUser"],
"getters": ["fullName", "isAdmin"],
"persist": true
}
// stores/user.ts
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
interface User {
id: string;
name: string;
email: string;
role: 'user' | 'admin';
}
export const useUserStore = defineStore('user', () => {
// State
const user = ref<User | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
// Getters
const isAuthenticated = computed(() => !!user.value);
const isAdmin = computed(() => user.value?.role === 'admin');
const fullName = computed(() => user.value?.name ?? 'Guest');
// Actions
async function login(email: string, password: string) {
loading.value = true;
error.value = null;
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
throw new Error('Invalid credentials');
}
const data = await response.json();
user.value = data.user;
localStorage.setItem('token', data.token);
} catch (e) {
error.value = (e as Error).message;
throw e;
} finally {
loading.value = false;
}
}
async function logout() {
user.value = null;
localStorage.removeItem('token');
}
async function fetchUser() {
const token = localStorage.getItem('token');
if (!token) return;
loading.value = true;
try {
const response = await fetch('/api/auth/me', {
headers: { Authorization: `Bearer ${token}` },
});
user.value = await response.json();
} catch (e) {
logout();
} finally {
loading.value = false;
}
}
return {
// State
user,
loading,
error,
// Getters
isAuthenticated,
isAdmin,
fullName,
// Actions
login,
logout,
fetchUser,
};
});
// stores/cart.ts
import { defineStore } from 'pinia';
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
}),
getters: {
totalItems: (state) =>
state.items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: (state) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
isEmpty: (state) => state.items.length === 0,
},
actions: {
addItem(item: Omit<CartItem, 'quantity'>) {
const existing = this.items.find((i) => i.id === item.id);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...item, quantity: 1 });
}
},
removeItem(id: string) {
const index = this.items.findIndex((i) => i.id === id);
if (index > -1) {
this.items.splice(index, 1);
}
},
updateQuantity(id: string, quantity: number) {
const item = this.items.find((i) => i.id === id);
if (item) {
item.quantity = Math.max(0, quantity);
if (item.quantity === 0) {
this.removeItem(id);
}
}
},
clearCart() {
this.items = [];
},
},
});
// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import App from './App.vue';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
const app = createApp(App);
app.use(pinia);
app.mount('#app');
// Store with persistence
export const useSettingsStore = defineStore('settings', {
state: () => ({
theme: 'light',
language: 'en',
}),
persist: true, // Persists to localStorage
});
// Custom persistence config
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
token: null,
}),
persist: {
key: 'user-store',
storage: sessionStorage,
paths: ['token'], // Only persist token
},
});
// stores/checkout.ts
import { defineStore } from 'pinia';
import { useCartStore } from './cart';
import { useUserStore } from './user';
export const useCheckoutStore = defineStore('checkout', () => {
const cart = useCartStore();
const user = useUserStore();
const canCheckout = computed(() => {
return user.isAuthenticated && !cart.isEmpty;
});
async function processCheckout(paymentMethod: string) {
if (!canCheckout.value) {
throw new Error('Cannot checkout');
}
const order = {
userId: user.user!.id,
items: cart.items,
total: cart.totalPrice,
paymentMethod,
};
const response = await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(order),
});
if (response.ok) {
cart.clearCart();
}
return response.json();
}
return {
canCheckout,
processCheckout,
};
});
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
// Destructure with reactivity preserved
const { user, isAuthenticated, loading } = storeToRefs(userStore);
// Actions can be destructured directly
const { login, logout } = userStore;
async function handleLogin() {
try {
await login(email.value, password.value);
router.push('/dashboard');
} catch (e) {
// Handle error
}
}
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="isAuthenticated">
Welcome, {{ user?.name }}
<button @click="logout">Logout</button>
</div>
<LoginForm v-else @submit="handleLogin" />
</template>
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.