Pinia store usage error
March 26, 2025 Β· AI Conversations
See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.
This will fail in production.
at useStore (pinia.js?v=a45335ab:1299:13)
at myPortfolio.ts:3:19
### π§ Common Fixes
#### β
1. **Ensure Pinia is properly initialized**
Make sure you're setting up Pinia in your `main.ts` or `main.js` file like this:
// main.ts or main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia); // β
Important!
app.mount('#app');
#### β
2. **Avoid using the store outside components or `setup()`**
If youβre importing a store at the top level of a file (outside of a component or setup function), youβll see this error.
For example, this is β wrong:
// myPortfolio.ts
import { usePortfolioStore } from '@/stores/portfolio'; // β BAD outside setup
const store = usePortfolioStore(); // β will throw "getActivePinia" error
Instead, do this β inside a component or composable thatβs called after the app is mounted:
// myPortfolio.ts
import { usePortfolioStore } from '@/stores/portfolio';
export function useMyPortfolio() {
const store = usePortfolioStore(); // β
OK inside a function
return store.portfolio;
}
Then call `useMyPortfolio()` **after** the app is mounted (e.g. in a componentβs `setup()` or inside a Vue lifecycle hook).
—
If you show me how you're using `usePortfolioStore` in `myPortfolio.ts`, I can help you refactor it correctly.
import { createApp } from 'vue'
import vuetify from './vuetify'
import { createPinia } from 'pinia'
import { API_URL } from '@/config'
import App from './App.vue'
import router from './router'
import TheAlert from './components/TheAlert.vue'
import IconLoading from './components/icons/IconLoading.vue'
import OverlayLoading from './components/OverlayLoading.vue'
import '@/scss/style.scss';
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar';
import VueApexCharts from 'vue3-apexcharts';
import VueTablerIcons from 'vue-tabler-icons';
// print
import print from 'vue3-print-nb';
const app = createApp(App)
app.use(VueTablerIcons);
app.use(print);
app.use(VueApexCharts);
app.use(router)
app.use(PerfectScrollbarPlugin);
app.use(createPinia())
app.use(vuetify)
app.component('TheAlert', TheAlert);
app.component('IconLoading', IconLoading);
app.component('OverlayLoading', OverlayLoading);
app.provide('apiUrl', API_URL);
app.mount('#app')
You're calling `app.use(createPinia())` directly, which creates a new Pinia instance **in place**, but doesn't let you access it later to set it as the **active Pinia** for usage outside of components.
This is exactly why you're getting the error:
"getActivePinia()" was called but there was no active Pinia
—
### β Fix
Change this line:
app.use(createPinia())
To this:
const pinia = createPinia();
app.use(pinia);
Now if you **need** to access the store outside Vue components (like in `myPortfolio.ts`), Pinia will know which instance is active.
—
### Bonus: Optional `setActivePinia` (for outside usage)
If you must use the store outside component setup (like in composables or utility files), then also do this in `myPortfolio.ts`:
import { usePortfolioStore } from '@/stores/portfolio';
import { setActivePinia, createPinia } from 'pinia';
setActivePinia(createPinia()); // OR pass the existing pinia instance if accessible
const store = usePortfolioStore();
β οΈ Just make sure you only do this **in rare cases**, as ideally you should call `useStore()` inside components or composables after app init.
—
Let me know how you're using `myPortfolio.ts` and I can help you structure it cleanly.
import { db, storage } from '@/firebase'
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
import {
collection,
addDoc,
getDocs,
doc,
deleteDoc,
updateDoc,
} from 'firebase/firestore'
import {
ref as storageRef,
uploadBytes,
getDownloadURL,
deleteObject,
} from 'firebase/storage'
const portfolioCollection = collection(db, 'portfolio');
const portfolioCategoryCollection = collection(db, 'portfolioCategories');
export const addPortfolioItem = async ({ title, description, url, imageFile, categoryId}: { title:string, description:string, url:string, imageFile:File, categoryId:string}) => {
try {
const imgPath = `portfolio/${Date.now()}_${imageFile.name}`
const imgRef = storageRef(storage, imgPath)
await uploadBytes(imgRef, imageFile)
const imageUrl = await getDownloadURL(imgRef)
const docRef = await addDoc(portfolioCollection, {
title,
description,
url,
image: imageUrl,
imagePath: imgPath,
categoryId,
provider_id: authStore.user?.provider_id,
createdAt: new Date(),
// Store for deletion later if needed
})
return docRef.id
} catch (error) {
console.error('Error adding portfolio item:', error)
throw error
}
}
export const deletePortfolioItem = async (id: string) => {
try {
const docRef = doc(db, 'portfolio', id)
await deleteDoc(docRef)
} catch (error) {
console.error('Error deleting portfolio item:', error)
throw error
}
}
export const fetchPortfolioItems = async () => {
try {
const snapshot = await getDocs(portfolioCollection)
return snapshot.docs.map(doc => ({
id: doc.id,
…doc.data(),
}))
console.log('Fetched items:', snapshot);
} catch (error) {
console.error('Error fetching portfolio items:', error)
return []
}
}
export const updatePortfolioItem = async ({ id, title, description, url, imageFile, categoryId }: { id: string, title: string, description: string, url: string, imageFile: File, categoryId: string }) => {
try {
const docRef = doc(db, 'portfolio', id)
await updateDoc(docRef, {
title,
description,
url,
image: imageFile ? await uploadBytes(storageRef(storage, `portfolio/${Date.now()}_${imageFile.name}`), imageFile) : undefined,
imagePath: imageFile ? `portfolio/${Date.now()}_${imageFile.name}` : undefined,
provider_id: authStore.user?.provider_id,
categoryId,
updatedAt: new Date()
})
} catch (error) {
console.error('Error updating portfolio item:', error)
throw error
}
}
export const fetchPortfolioCategories = async () => {
try {
const snapshot = await getDocs(portfolioCategoryCollection)
return snapshot.docs.map(doc => ({
id: doc.id,
…doc.data(),
}))
console.log('Fetched items:', snapshot);
} catch (error) {
console.error('Error fetching portfolio items:', error)
return []
}
}
export const addPortfolioCategory = async ({ name,imageFile,description,url }: { name: string, imageFile: File, description: string, url: string }) => {
try {
const imgPath = `portfolio/${Date.now()}_${imageFile.name}`
const imgRef = storageRef(storage, imgPath)
await uploadBytes(imgRef, imageFile)
const imageUrl = await getDownloadURL(imgRef)
const docRef = await addDoc(portfolioCategoryCollection, {
name,
image: imageUrl,
imagePath: imgPath,
description,
url,
createdAt: new Date(),
})
return docRef.id
} catch (error) {
console.error('Error adding portfolio category:', error)
throw error
}
}
export const deletePortfolioCategory = async (id: string) => {
try {
const docRef = doc(db, 'portfolioCategories', id)
await deleteDoc(docRef)
} catch (error) {
console.error('Error deleting portfolio category:', error)
throw error
}
}
export const updatePortfolioCategory = async ({ id, name, imageFile, description }: { id: string, name: string, imageFile: File, description: string }) => {
try {
const docRef = doc(db, 'portfolioCategories', id)
await updateDoc(docRef, {
name,
description,
image: imageFile ? await uploadBytes(storageRef(storage, `portfolio/${Date.now()}_${imageFile.name}`), imageFile) : undefined
})
} catch (error) {
console.error('Error updating portfolio category:', error)
throw error
}
}
const authStore = useAuthStore(); // β this causes "getActivePinia" error
Since this file is imported somewhere early (likely before the app is mounted), and `useAuthStore()` internally depends on Pinia being initialized, it throws the `getActivePinia()` error.
—
### β Solution: Lazy-load the store inside functions
Instead of initializing the store at the top level, **move it inside the functions where itβs actually used**:
#### π Replace this:
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore(); // β Immediate execution
#### β With this:
import { useAuthStore } from '@/stores/auth';
Then inside your function:
export const addPortfolioItem = async ({ title, description, url, imageFile, categoryId }) => {
const authStore = useAuthStore(); // β
called after Pinia is active
...
}
Same thing inside `updatePortfolioItem`:
const authStore = useAuthStore(); // call it here, not globally
—
### π Why it works
Calling `useAuthStore()` at the top level executes before `app.use(createPinia())` runs. But calling it **inside a function or setup()** means it runs only when the function is used β which is after Pinia is mounted.
—
Would you like me to refactor the file for you with this fix applied throughout?
get inistials of name in vue
—
### β
Option 1: **Inside a “ component**
If you're using “ with Composition API:
<script setup>
import { useAuthStore } from '@/stores/auth';
import { computed } from 'vue';
const authStore = useAuthStore();
const userInitials = computed(() => {
const name = authStore?.user?.name || '';
return name
.split(' ')
.map(part => part.charAt(0).toUpperCase())
.join('');
});
</script>
<template>
<div>{{ userInitials }}</div>
</template>
—
### β Option 2: **Method (if needed in JS logic)**
If you need it inside a function:
function getInitials(name: string): string {
return name
.split(' ')
.map(word => word.charAt(0).toUpperCase())
.join('');
}
// Usage
const initials = getInitials(authStore?.user?.name || '');
—
### π Example
| Name | Initials |
|——————|———-|
| `Neeraj Tiwari` | `NT` |
| `John D. Smith` | `JDS` |
| `Amit` | `A` |
Let me know if you want to limit it to just **first and last** names instead of all parts.
—
## β Option 1: **Global Helper Function (Recommended for Utility)**
1. **Create a file** `src/utils/stringHelpers.ts` (or `.js`):
// stringHelpers.ts
export function getInitials(name: string): string {
return name
.split(' ')
.map(word => word.charAt(0).toUpperCase())
.join('');
}
2. **Import and use anywhere**:
import { getInitials } from '@/utils/stringHelpers';
const initials = getInitials(authStore?.user?.name || '');
> Clean and tree-shakable. Ideal for utility functions.
—
## β Option 2: **Add to Global Properties (if you want to access in templates without importing)**
1. Modify your `main.ts`:
import { createApp } from 'vue';
import App from './App.vue';
import { getInitials } from '@/utils/stringHelpers';
const app = createApp(App);
// Add to global properties
app.config.globalProperties.$getInitials = getInitials;
app.mount('#app');
2. In any component (template or script):
<template>
<div>{{ $getInitials(authStore.user.name) }}</div>
</template>
<script setup>
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
</script>
> β Best when you want `getInitials()` available everywhere in templates without imports.
—
## β οΈ Option 3: **Global Mixin** (Not recommended in Vue 3)
Global mixins work but can pollute global scope and increase bundle size unnecessarily. Use Option 1 or 2 instead.
—
Let me know which way you prefer (template use or import-based), and I can generate the exact files for you.
// stores/auth.ts
import { defineStore } from 'pinia';
interface User {
id: number;
name: string;
email: string;
avatar: string;
mobile_no:string;
provider_id:string;
provider:string;
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: localStorage.getItem('user') ? (JSON.parse(localStorage.getItem('user')!) as User) : null,
token: localStorage.getItem('token') || null,
}),
getters: {
isAuthenticated: (state) => !!state.token, // Returns true if token exists
},
actions: {
setUser(user: User, token: string) {
this.user = user;
this.token = token;
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
}
}
});
—
### β Updated `stores/auth.ts` with `userInitials` getter:
// stores/auth.ts
import { defineStore } from 'pinia';
interface User {
id: number;
name: string;
email: string;
avatar: string;
mobile_no: string;
provider_id: string;
provider: string;
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: localStorage.getItem('user') ? (JSON.parse(localStorage.getItem('user')!) as User) : null,
token: localStorage.getItem('token') || null,
}),
getters: {
isAuthenticated: (state) => !!state.token,
userInitials: (state) => {
const name = state.user?.name || '';
return name
.split(' ')
.map(word => word.charAt(0).toUpperCase())
.join('');
},
},
actions: {
setUser(user: User, token: string) {
this.user = user;
this.token = token;
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
},
},
});
—
### β Usage in any component:
<template>
<div>Initials: {{ authStore.userInitials }}</div>
</template>
<script setup>
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
</script>
Let me know if youβd prefer to limit initials to **first and last name only** or apply a max length (e.g., 2 characters).
—
### β Here's how to use `computed()` with `useAuthStore()` in a component:
<script setup lang="ts">
import { computed } from 'vue';
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
const userInitials = computed(() => {
const name = authStore.user?.name || '';
return name
.split(' ')
.map(part => part.charAt(0).toUpperCase())
.join('');
});
</script>
<template>
<div>Initials: {{ userInitials }}</div>
</template>
—
### π§ When to use this vs Pinia getter?
| Approach | Best When… |
|—————-|————–|
| **Pinia Getter** (`userInitials`) | You want initials available **globally across app** via the store |
| **Local `computed()`** | You need initials in **just one component or composable** |
Both are valid β choose based on reusability needs.
Let me know if you want to make a global **composable** for it (like `useUserInitials()`) instead!