Skip to content
advanced Phase 7 · Lifecycles and Composables

Shared State Across Component Instances

Decide when a composable should create state per call or share one instance.

40m
0 problems
Topic Progress 0%

Shared State Across Component Instances: Core Concepts

State location defines sharing

Refs declared inside a composable function are created for each invocation. Two components calling useCounter() receive independent values unless the composable deliberately shares state. A ref declared at module scope is one singleton shared by every caller of that module instance. This changes lifetime, reset behavior, test isolation, and server-rendering safety.

Per-call state fits local form state, a component-specific request, or a widget whose values should disappear with its owner. Module-scoped state can be appropriate for an intentional client-only shared preference, but it persists beyond component unmount and has no automatic reset. If state needs coordinated actions, persistence, debugging, or cross-view lifecycle, Pinia or an app-scoped service may express ownership more clearly.

SSR makes module state dangerous

An SSR server can reuse a module across multiple requests. A mutable module-level ref may therefore retain one user's data while another user's page renders. Create request-specific stores inside an app factory or per-request context. Never put authentication details, user-specific resources, or request-scoped data in a shared server module singleton.

Client-only shared state also needs a lifecycle policy. Decide when it initializes, whether it persists through refresh, how logout clears it, and what happens when several components update it. A global value without a reset path can make tests order-dependent and leak stale user data across sessions. Provide explicit actions for reset and write operations.

Expose a controlled contract

A shared store should expose readonly state and named actions that preserve invariants. If consumers mutate refs freely, they can create invalid combinations. For example, changing the selected account without updating derived permissions may expose stale UI. Keep related transitions in one action and derive secondary values with computed properties.

Distinguish shared client state from server data cache. A query library may own request deduplication, freshness, and invalidation. A store may own a multi-view workflow or client preference. Duplicating every server response in both can create competing sources of truth. Use the smallest mechanism that fits the state lifetime and synchronization requirement.

Reset and resource ownership

Unmounting a component does not reset module-level refs. If state should reset when the last consumer leaves, a reference-counted lifetime is possible but adds complexity and needs concurrency tests. Often an explicit store action on logout or feature exit is clearer. State reset should include subscriptions and cached data tied to the previous identity.

Shared composables may own timers, sockets, or event listeners. A view must not close a shared connection another view still needs. Place shared resources in an app-level service and define who shuts them down. Keep resource lifetime and state lifetime aligned or explicitly separate them.

The state-scope rule is straightforward: inside the function means per invocation; module scope means a shared singleton; an app-scoped store means deliberate application-wide coordination. Each choice has SSR, persistence, test, and reset consequences that should be part of the design.

For app-scoped state, prefer an explicit store factory or app-provided instance when the code can run on a server. This makes the lifetime visible and allows separate app instances in tests.

Shared State Across Component Instances: Practice

Compare independent and shared counters

Implement useCounter with a ref declared inside the function and render two components. Each should increment independently. Move the ref to module scope and repeat: both views now observe one shared count, and unmounting one view does not reset it. Write a test that states which behavior the feature requires so moving a declaration does not silently change the API.

For a shared theme preference, a module singleton can be reasonable in a client-only app if initialization, persistence, and reset are explicit. Return a readonly computed theme and a setTheme action. Validate any persisted value before using it. Do not use this convenience for access tokens or user permissions; client storage is mutable and is not an authorization boundary.

Scope user state per app

If several views need account data or an authenticated workflow, create a store instance per Vue app. In SSR, create that store during the request-specific app factory so concurrent requests cannot share state. Test two app instances with different users and assert their values remain isolated. Avoid importing a pre-created mutable store singleton into a server module.

Use named actions to preserve invariants. setAccount can update the account and related status together; clearSession can remove user data, query caches, and subscriptions. Components can read state without being able to create a partially valid combination. If server data is owned by a query cache, avoid copying every response into another store unless the application has a specific coordination need.

Define persistence and reset

Write down which state survives component unmount, route changes, browser refresh, and logout. Local interaction state typically ends with the component. App-scoped state can survive navigation. Persist only values that should survive a refresh, and define schema/version handling for stored data. Clear user-scoped state when identity changes and test that a second user cannot see the prior session's content.

If shared state owns a socket or timer, test shutdown from the owner rather than from a random consumer. Multiple components may subscribe; closing the transport when one view disappears can break the rest. A service can maintain subscriptions and stop when the app or authenticated session ends. Keep the ownership rule visible in its API.

Test shared consumers and SSR

Mount two consumers and verify updates propagate as designed. Unmount one and confirm the other remains functional. Reset state and confirm every subscriber observes the reset. Create two SSR app instances and ensure state does not cross requests. Restore module state between unit tests or expose a reset function for intentional singletons.

Shared state is not automatically better than duplication. If two components use the same value but do not need coordinated updates or common lifetime, per-call state may be simpler. If the state has server freshness and invalidation needs, a query cache may be the right owner. Choose a scope, test its lifecycle, and keep one authoritative source.

If migrating module state into a store, preserve the public behavior deliberately: identify which values are shared, which actions update them, and when they reset. Add tests before changing ownership so components do not accidentally receive isolated state where sharing was required.

Shared State Across Component Instances: Trade-offs and Interviews

Singleton convenience adds coupling

Module-scoped refs make sharing easy but create global mutable state. Callers become coupled, tests may affect one another, and a server module can leak data between SSR requests. Use module scope only when the shared lifetime is intentional and safe. Add explicit reset and write methods rather than exposing unrestricted mutable state.

Per-call state gives isolation but can duplicate requests or copies. Pinia adds explicit app-level state and devtools support; a query cache adds freshness, deduplication, and invalidation for server data. These tools solve different problems. Do not put all values in a global store simply because two components read them.

Make privacy and lifecycle explicit

User-specific state must be scoped to the app or request and cleared on identity changes. Persistent storage should contain only approved values and must not be trusted for authorization. Shared sockets and timers need an owner separate from individual component consumers when they outlive one view. Test reset, logout, app disposal, and concurrent SSR requests.

In an interview, explain where the ref is declared, who shares it, how long it lives, and how it resets. Describe why module state is unsafe for per-user SSR data and when a store or query cache is preferable. This shows that scope and lifetime are architectural decisions, not incidental consequences of moving a ref outside a function.

Module singletons are difficult to reset in hot reload and automated tests. Exporting a reset method can help, but it should not become a production backdoor that clears state without authorization. Prefer constructing a fresh store instance for each test and each server request. If a client cache is intentionally global, give it a documented clear operation tied to logout and data invalidation.

Multiple consumers introduce concurrency. If one component begins a request and another begins a newer one, define whether both responses may update shared state or only the latest request wins. Use request keys, cancellation, or a query cache to avoid one consumer overwriting another's result. Keep loading and error state scoped to the request when different views can be active simultaneously.

Persistence is a separate dimension from sharing. A module singleton is shared in memory but does not survive a browser refresh; local storage persists but is writable and may be stale; a server cache has its own invalidation and tenant-isolation needs. Choose storage by lifetime and trust boundary. Do not equate globally reachable state with durable state or authorization.

When state is shared across routes, identify the point where it becomes invalid: logout, tenant switch, account change, or server mutation. Clear dependent caches and subscriptions together. Add a test for switching identities in one app instance, because that is where stale data from the previous scope can become a privacy bug.

The best abstraction is the narrowest scope that matches the feature. Keep state local until multiple consumers genuinely need coordination; then promote it to an app-scoped owner with actions, reset, and test isolation. This avoids both duplicated sources of truth and a global store filled with unrelated values.

Quiz

1. Which statement about Shared State Across Component Instances is accurate?

Question 1 options

2. When reviewing Shared State Across Component Instances, which point must hold?

Question 2 options

3. What does a module-scoped ref inside a composable module create?

Question 3 options

4. Why create user state per SSR request?

Question 4 options

Flashcards

Question

What is Shared State Across Component Instances?

Answer

Decide when a composable should create state per call or share one instance.

Question

Shared State Across Component Instances: why it matters

Answer

Refs declared at module scope inside a composable act as a single shared singleton store.

Question

Shared State Across Component Instances: key detail

Answer

Refs declared inside the function body give every caller an independent copy of the state.

Question

Shared State Across Component Instances: practical note

Answer

Shared composable state resets only on full page reload, not on component unmount.

Question

What is the effect of declaring a ref at module scope in a composable module?

Answer

Module-scope state is shared across callers and persists independently of individual component lifetimes.

Revision Notes

Key Takeaways

  • 1. Refs declared at module scope inside a composable act as a single shared singleton store.
  • 2. Refs declared inside the function body give every caller an independent copy of the state.
  • 3. Shared composable state resets only on full page reload, not on component unmount.

Interview Tips

  • • Explain Shared State Across Component Instances with one concrete example from real vue work, not a textbook restatement.
  • • Discuss the trade-off: when Shared State Across Component Instances helps and when a lighter approach wins.
  • • Place Shared State Across Component Instances inside the Lifecycles and Composables phase — interviewers check that you see the progression, not isolated facts.

Cheat Sheet

Shared State Across Component Instances Cheat Sheet

Phase: Lifecycles and Composables | Level: advanced

Key points:

  • Refs declared at module scope inside a composable act as a single shared singleton store.
  • Refs declared inside the function body give every caller an independent copy of the state.
  • Shared composable state resets only on full page reload, not on component unmount.
  • Move truly shared state into Pinia when it must be inspected or reset by devtools.

Tags: vue, composables, state

Continue Learning

Before You Start

  • No prerequisites listed.

Related Reading