Files
stack/packages/mosaic/framework/skills/pinia/references/features-composing-stores.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

2.6 KiB

name, description
name description
composing-stores Store-to-store communication and avoiding circular dependencies

Composing Stores

Stores can use each other for shared state and logic.

Rule: Avoid Circular Dependencies

Two stores cannot directly read each other's state during setup:

// ❌ Infinite loop
const useX = defineStore('x', () => {
  const y = useY();
  y.name; // Don't read here!
  return { name: ref('X') };
});

const useY = defineStore('y', () => {
  const x = useX();
  x.name; // Don't read here!
  return { name: ref('Y') };
});

Solution: Read in getters, computed, or actions:

const useX = defineStore('x', () => {
  const y = useY();

  // ✅ Read in computed/actions
  function doSomething() {
    const yName = y.name;
  }

  return { name: ref('X'), doSomething };
});

Setup Stores: Use Store at Top

import { defineStore } from 'pinia';
import { useUserStore } from './user';

export const useCartStore = defineStore('cart', () => {
  const user = useUserStore();
  const list = ref([]);

  const summary = computed(() => {
    return `Hi ${user.name}, you have ${list.value.length} items`;
  });

  function purchase() {
    return apiPurchase(user.id, list.value);
  }

  return { list, summary, purchase };
});

Shared Getters

Call useStore() inside a getter:

import { useUserStore } from './user';

export const useCartStore = defineStore('cart', {
  getters: {
    summary(state) {
      const user = useUserStore();
      return `Hi ${user.name}, you have ${state.list.length} items`;
    },
  },
});

Shared Actions

Call useStore() inside an action:

import { useUserStore } from './user';
import { apiOrderCart } from './api';

export const useCartStore = defineStore('cart', {
  actions: {
    async orderCart() {
      const user = useUserStore();

      try {
        await apiOrderCart(user.token, this.items);
        this.emptyCart();
      } catch (err) {
        displayError(err);
      }
    },
  },
});

SSR: Call Stores Before Await

In async actions, call all stores before any await:

actions: {
  async orderCart() {
    // ✅ All useStore() calls before await
    const user = useUserStore()
    const analytics = useAnalyticsStore()

    try {
      await apiOrderCart(user.token, this.items)
      // ❌ Don't call useStore() after await (SSR issue)
      // const otherStore = useOtherStore()
    } catch (err) {
      displayError(err)
    }
  },
}

This ensures the correct Pinia instance is used during SSR.