# Vue 3 State Management Standards · VUE-04

Stores provide centralised, clear patterns for state management. To name one of the many benefits; by using stores for state, components can focus on presentation. Separating concerns in UI code promotes cleanliness and maintainability, which is crucial for creating large scale-applications.

## Standard Metadata

- Area: Vue
- Owner: Frontend Steering Group
- Last reviewed: 2026-09-09
- Next review due from: 2026-12-01
- Enforcement: Code review
- Status: Active

## Share State Across Components

### Store Structure and Modules

#### Use Centralised State Management · VUE-04.1 · **MUST**

When a composable's state must be used across multiple components, use the Composition API's provide/inject, or `Pinia` for state management.

This allows state passing down a component tree without `prop` drilling. For complex state management, use `Pinia`.

#### Organise Stores in the `src/stores` Directory · VUE-04.2 · **MUST**

Name subdirectories according to the module or feature they represent (e.g. `messages`, `auth`, `products`).

#### Prefix Store Names with `use` · VUE-04.3 · **MUST**

As stores are composables, their names must always start with use.

#### Suffix Store Names with `store` · VUE-04.4 · **SHOULD**

For organisation and easy identifications store names should end with store. Therefore they get a pragmatic name of `useChatStore`, `useShoppingBasketStore`, `useRegistryStore`, etc.

#### Define Simple Stores in a Single File · VUE-04.5 · **SHOULD**

Simple stores should be defined in a single file.

#### Define Complex Stores in Multiple Files· VUE-04.6 · **SHOULD**

For complex stores, split the state, getters, actions and mutations into separate files within a modular store directory.

## Simple State Management Patterns

### Use Vue Reactive Singletons to ensure a single source of truth · VUE-04.7 · **MUST**

Manage shared reactive state across multiple components using a singleton pattern, ensuring a single source of truth.

A reactive singleton is a simple, lightweight solution for managing shared state for small features or projects. Pinia on the other hand, is better suited for larger applications, requiring advanced statement management features such as plugins, devtools integrations and `store` modules.

A reactive class is created on app load, any changes to this class persist globally, this allows you to have a single, reactive source of truth for logic that can be easily imported and used in various parts of a Vue application.

Components can interact with this helper, and any changes will be reactively reflected wherever it's used.

#### Vue reactive Singleton Example

```ts
import {reactive} from 'vue';

/**
 * A singleton store for managing a counter.
 *
 * @returns {Object} An object containing:
 *   - {Object} state - The reactive state object.
 *   - {Function} increment - A function to increment the counter.
 *   - {Function} reset - A function to reset the counter.
 *
 * @example
 * import { counterStore } from '@/store/useCounterStore';
 * const { state, increment, reset } = counterStore;
 */
const state = reactive({
  count: 0,
});

function increment() {
  state.count++;
}

function reset() {
  state.count = 0;
}

export const counterStore = {
  state,
  increment,
  reset,
};
```

### Best Practices for State Management

### Ensure Shared State remains reactive · VUE-04.8 · **MUST**

Shared state must remain reactive by using either `ref` or `reactive` within composables.

#### Shared State Example

```ts
export function useCounter(initialValue = 0) {
  // Define a reactive reference for the counter value
  const count = ref(initialValue);

  function increment() {
    count.value++;
  }

  function reset() {
    count.value = initialValue;
  }

  return {count, increment, reset};
}
```

#### Do not introduce Global Side Effects · VUE-04.9 · **MUST**

Keep shared state management isolated, avoidind unintended side effects across components.

In the following example, the variable `globalCounter` introduces maintainability and scalability issues:

- No isolation leads to unintended side effects across any component which uses the composable.
- Changes to one component will affect all components using the composable.
- As the application grows, managing and tracking changes to the global state becomes cumbersome and error-prone.

##### Global Side Effect Example (❌)

```ts
import {ref} from 'vue';

// Bad practice
let globalCounter = ref(0);

export function useGlobalCounter() {
  function increment() {
    globalCounter.value++;
  }

  function reset() {
    globalCounter.value = 0;
  }

  return {count: globalCounter, increment, reset};
}
```
