Pinia store usage error

User
pinia.js?v=a45335ab:1299 Uncaught Error: [🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?
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
Assistant
This error usually occurs when you try to use a Pinia store **outside of a Vue component** or **before initializing Pinia with `app.use(pinia)`** in your main application setup.

### πŸ”§ 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.

User
import './assets/main.css'

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')

Assistant
Thanks for sharing your `main.ts` file β€” it looks like you're doing almost everything right. However, there's one **subtle but critical issue** here:

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.

User
this file cause error
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
}
}

Assistant
Thanks, Neeraj β€” the issue is caused by this line being executed **outside a Vue component or `setup()` function**, before Pinia has been initialized:

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?

User
authStore?.user?.name
get inistials of name in vue
Assistant
To get the **initials** from `authStore?.user?.name` in Vue, you can create a simple computed property or method depending on your use case.

### βœ… 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.

User
how to make it globally
Assistant
To make a **"get initials from name"** utility globally available in your Vue 3 app, you have a few clean options. Here's how to do it the right way, depending on your preferred structure:

## βœ… 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.

User
Add here computed
// 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');
}
}
});

Assistant
You can add a **computed getter** inside your `useAuthStore` for initials based on the user's name. Here's how to do it:

### βœ… 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).

User
use computed method
Assistant
Got it! You’d like to compute the initials **using the `computed()` method** instead of a Pinia getter. This is great when you're inside a Vue component or composable and want to keep logic local instead of in the store.

### βœ… 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!

Leave a Comment

Your email address will not be published. Required fields are marked *