Pinia

caution
Pilot Phase: Adopt Pinia with caution. This is a new technology at GitLab and we might not have all the necessary precautions and best practices in place yet. If you’re considering using Pinia please drop a message in the #frontend internal Slack channel for evaluation.

Pinia is a tool for managing client-side state for Vue applications. Refer to the official documentation on how to use Pinia.

Best practices

Small stores

Prefer creating small stores that focus on a single task only. This is contrary to the Vuex approach which encourages you to create bigger stores.

Treat Pinia stores like cohesive components rather than giant state façades (Vuex modules).

Single file stores

Place state, actions, and getters in a single file. Do not create ‘barrel’ store index files which import everything from actions.js, state.js and getters.js.

If your store file gets too big it’s time to consider splitting that store into multiple stores.

Use Option Stores

Pinia offers two types of store definitions: option and setup. Prefer the option type when creating new stores. This promotes consistency and will simplify the migration path from Vuex.

Before Pinia

flowchart TD A[Store] A --> B[State] A --> C[Actions] A --> D[Mutations] A --> E[Getters] B --> F[items] B --> G[isLoadingItems] B --> H[itemWithActiveForm] B --> I[isSubmittingForm]

After Pinia

flowchart TD A[Items Store] A --> B[State] A --> C[Actions] A --> D[Getters] B --> E[items] B --> F[isLoading] H[Form Store] H --> I[State] H --> J[Actions] H --> K[Getters] I --> L[activeItem] I --> M[isSubmitting]

Migrating from Vuex

Decide what your primary state manager should be first. Proceed with this guide if Pinia was your choice.

Follow the official Vuex migration guide.

Try to split all your migrations into two steps:

  1. Refactor just the Vuex API: Don’t change the store structure, make sure it works in Pinia (example).
  2. Refactor the structure: Split your store into multiple smaller, single purpose stores.

Migration plan

If your store can’t be migrated in a single merge request then follow these steps for a smooth migration from Vuex to Pinia:

  1. Identify the stores you are going to migrate. Include all the stores that depend on your store and all the stores that you depend on.
  2. Create a migration issue, assign a migration DRI(s) and list all the stores you’re going to migrate. Track your migration progress in that issue. If necessary, split the migration into multiple issues.
  3. Create a new CODEOWNERS (.gitlab/CODEOWNERS) rule for the store files you’re migrating, include all the Vuex module dependencies and store specs.

    If you are migrating only a single store then you would need to include only state.js (or your index.js), actions.js, mutations.js and getters.js and their respective spec files.

    Assign at least two individuals responsible for reviewing changes made to the Vuex store. Always sync your changes from Vuex store to Pinia. This is very important so you don’t introduce regressions with the Pinia store.

  4. Copy existing store as-is to a new location (you can call it stores/legacy_store for example). Keep the file structure intact.
  5. Create a store definition (defineStore) and only define your state in there.
  6. Use code mods from the next section to migrate the store files. Import and use the migrated code in store’s definition.
  7. Migrate the store specs manually.
  8. Refactor components to use the new store.
  9. Remove the Vuex store.
  10. Remove CODEOWNERS rule.
  11. Close the migration issue.

Automated migration using codemods

You can use ast-grep codemods to simplify migration from Vuex to Pinia.

  1. Install ast-grep on your system before proceeding.
  2. Run scripts/frontend/codemods/vuex-to-pinia/migrate.sh path/to/your/store

The codemods will migrate actions.js, mutations.js and getters.js located in your store folder. Manually scan these files after running the codemods to ensure they are properly migrated. Vuex specs can not be automatically migrated, migrate them by hand.

Vuex module calls are replaced using Pinia conventions:

  1. dispatch('anotherModule/action', ...args, { root: true })useAnotherModule().action(...args)
  2. dispatch('action', ...args, { root: true })useRootStore().action(...args)
  3. rootGetters['anotherModule/getter']useAnotherModule().getter
  4. rootGetters.getteruseRootStore().getter
  5. rootState.anotherModule.stateuseAnotherModule().state

If you have not yet migrated a dependent module (useAnotherModule and useRootStore in the examples above) you can create a temporary dummy store. Use the guidance below to migrate Vuex modules.

Migrating stores with nested modules

It is not trivial to iteratively migrate stores with nested modules that have dependencies between them. In such cases prefer migrating nested modules first:

  1. Create a Pinia store counterpart of the nested Vuex store module.
  2. Create a placeholder Pinia ‘root’ store for root module dependencies if applicable.
  3. Copy and adapt existing tests for the migrated module.
  4. Do not use migrated modules yet.
  5. Once all the nested modules are migrated you can migrate the root module and replace the placeholder store with the real one.
  6. Replace Vuex store with Pinia stores in components.