DEV Community

Cover image for State management in Vue: Composition API vs. Pinia
Jochen Hörmann
Jochen Hörmann

Posted on

State management in Vue: Composition API vs. Pinia

The days of Vuex as Vue’s default state-management tool are long gone and Pinia has become the new standard. It provides well built functionality for global state-management. But do you even need a dedicated state-management library? And if not, how do you decide?

Vue's Composition API as state-management alternative

With Vue 3's Composition API there is a built-in alternative for state-management By using the built in functions you can organize your code and share data between components and component trees only where you need it. This approach provides some clear benefits:

  • Only provide state where needed
  • No additional setup required
  • No additional dependency needed
  • Keep different parts of your software separated.

Global state

Traditional global state infrastructure

What is a "Composable"?

Composables make use of the provided Composition API methods (refs, reactive, computed, lifecycle hooks, etc.). See https://vuejs.org/guide/reusability/composables for in-depth information.

Composables

Composable infrastructure

Example

By knowing the following patterns you can build the same systems as with a traditional state-management system with composables.
In this example you can have multiple user stores that can have their state and load users with the help of the Composition API. You can even use the Vue Lifecycle Hooks to automatically fetch the users in the users view. When providing attributes to the use users function you can customize the different user stores e.g. by using feature flags.

Example useUsers.ts helper:

import { ref, onMounted } from 'vue'
import type { Ref } from 'vue'
import { usersRepository } from '../api'
import type { User } from '../types'

export interface UsersHelper {
    users: Ref<User[]>
    loadUsers(): Promise<void>
}

export default (): UsersHelper => {
    // State
    const users = ref<User[]>([])

    // Actions e.g. load from remote resource
    const loadUsers = async (): Promise<void> => {
        users.value = await usersRepository.load()
    }

    // Trigger load when helper is called in component (optional)
    onMounted(() => {
        void loadUsers()
    })

    return { users, loadUsers }
}
Enter fullscreen mode Exit fullscreen mode

Use inject / provide instead of a global store

In conjunction with using composables as the main place for your state you should use the built in inject & provide functions to pass the state from parent to child components (where needed) in the same component tree.

Example

Setup injection key (injection-keys.ts)

import type { InjectionKey } from 'vue'
import type { UsersHelper } from './useUsers'

export const usersHelperKey: InjectionKey<UsersHelper> = Symbol('usersHelper')
Enter fullscreen mode Exit fullscreen mode

Provide in parent component

import { provide } from 'vue'
import useUsers from './useUsers'
import { usersHelperKey } from './injection-keys'

const usersHelper = useUsers({ autoLoad: true })
provide(usersHelperKey, usersHelper)
Enter fullscreen mode Exit fullscreen mode

Inject in child component

import { inject } from 'vue'
import { usersHelperKey } from './injection-keys'

const usersHelper = inject(usersHelperKey)
Enter fullscreen mode Exit fullscreen mode

Why you'd still want to use Pinia

  1. Easy Global Data Handling:
    Pinia is registered globally. That means that you can always access the stores throughout your app. Which makes data handling very convenient.

  2. Effective Debugging with Vue DevTools:
    Pinia integrates well with Vue DevTools, offering detailed support. It provides clearer insights into state changes, making debugging and troubleshooting smoother.

  3. Patterns & predictability
    For large-scale apps with many independent modules and teams, Pinia's clear structure can increase predictability.

Verdict

Vue 3’s Composition API gives you a powerful, dependency-free way to organize and share state. Pinia offers clear patterns for organizing and accessing global state. For new projects, consider using only composables with provide/inject instead of defaulting to a separate state-management library.

Top comments (0)