Store-to-Store Communication
Stores in geajs are exported as singletons — a single, shared instance per module, not a class the caller instantiates. This is the key fact that shapes how Stores should talk to each other: because every consumer that imports a given Store module gets the exact same instance, there is never any ambiguity about "which instance am I talking to." Stores can, and are meant to, import each other directly.
An Example: A Warehouse Dashboard
Consider an internal dashboard with two independent concerns: tracking inventory levels, and tracking which shipments are currently in transit. They're separate Stores because they're conceptually distinct and could each be useful without the other — but a shipment arriving needs to update inventory.
// inventory-store.ts
import { Store } from '@geajs/core'
class InventoryStore extends Store {
stock: Record<string, number> = {}
receive(sku: string, quantity: number) {
this.stock[sku] = (this.stock[sku] ?? 0) + quantity
}
}
export default new InventoryStore()
// shipments-store.ts
import { Store } from '@geajs/core'
import inventoryStore from './inventory-store'
class ShipmentsStore extends Store {
inTransit: { sku: string; quantity: number; eta: string }[] = []
markArrived(sku: string) {
const shipment = this.inTransit.find(s => s.sku === sku)
if (!shipment) return
inventoryStore.receive(shipment.sku, shipment.quantity)
this.inTransit = this.inTransit.filter(s => s !== shipment)
}
}
export default new ShipmentsStore()
ShipmentsStore imports inventoryStore directly and calls a method on it. No service layer, no context, no dependency injection — just a normal module import, because inventoryStore is guaranteed to be the one and only instance that exists anywhere in the app.
This also composes naturally with computed values: a getter on one Store can freely read state from another singleton Store it imports, the same way markArrived calls a method on one.
When to Split Into Multiple Stores
Split state into domain-specific Stores when different concerns are genuinely independent — inventory and shipment tracking, as above, are a natural fit for two separate Stores that reference each other. Keep a single Store when the state is small and cohesive, like a counter or a todo list — splitting it further would only add indirection without a corresponding benefit. See Sizing and Scoping a Store for the general guidance on where to draw that line.
Sharing State Across a Component Tree Without a Store
Not every case of "components far apart need the same data" requires a singleton Store. Two patterns built directly into props already cover a lot of this ground:
- Shared reactive objects as props. When a parent passes an object or array as a prop, the child receives the same reactive proxy — not a copy. A descendant can mutate it directly, and the change is visible to the parent, siblings, and any other component observing that data, with no callback wiring required.
- Passing components as props. For layouts where a distant region of the tree needs to render content decided elsewhere — the equivalent of "slots" in other frameworks — pass a component instance as a prop rather than reaching for a tree-spanning lookup mechanism.
Between singleton Stores for genuinely global/cross-cutting state, and these two prop-based patterns for tree-local sharing, most applications never need anything beyond what's already described on this page.
An earlier revision of this page recommended routing Store-to-Store communication through a dedicated service layer, and reaching for a context-provider-style library for cross-tree sharing. Neither reflects geajs's actual design: Stores are singletons meant to import each other directly, and geajs's philosophy explicitly does not include context providers as a supported pattern, with no exception for advanced use cases. This page has been corrected to match the official approach.