Skip to content

refactor: replace Vuex with Pinia - #3847

Open
Vondry wants to merge 5 commits into
bolt:6.2from
Vondry:refactor/vuex-to-pinia
Open

Vondry wants to merge 5 commits into
bolt:6.2from
Vondry:refactor/vuex-to-pinia

Conversation

@Vondry

@Vondry Vondry commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Next step in splitting #3751 towards Vue 3: Vuex 3 is replaced with Pinia 2, which runs on both Vue 2.7 and Vue 3. The admin stays on Vue 2.7 and the Options API, so templates are unchanged.

The store files are identical to #3751. Afterwards, the store part of #3751 is only: bump pinia to 3, switch to app.use(createPinia()), and optionally move components to useXStore() in <script setup>.

What changed

  • Each app's store/modules/* (state, getters, mutations, actions) becomes one typed defineStore file: listing, sidebar and toolbar. Pass-through getters and mutations are gone: components read with mapState and assign state directly. Only select/deSelect remain as actions.
  • Vue 2 only, removed again with Pinia 3:
    • Components pass this.$pinia when they call a store outside mapState (useXStore(this.$pinia)). The toolbar, sidebar and listing are separate Vue roots with their own Pinia, and in Vue 2.7 watchers and handlers a bare useXStore() would use the Pinia of whichever root booted last. Covered by tests/unit/store/multi-root.spec.ts.
    • vue-demi is aliased to its 2.7 build in webpack and Vitest, so installs that skip postinstall scripts still compile.

Commits

  • 22cf6504 refactor: replace Vuex with Pinia: straight port, no behaviour change
  • 875e9f82 fix(listing): derive the selected count from the selection: the count could drift from the selected ids; it's now a getter, and select() skips duplicates (as in Migrate to Vue 3 Composition API and TypeScript #3751)
  • 1cf1e213 chore: remove unused stores: the editor store and the listing type module were never read
  • 84905137 refactor(sidebar): read the slim flag with mapState: the one component not using mapState for reads
  • 422ee47d docs(listing): mark the order computed in SelectBox as dead code: a leftover of the unfinished drag-and-drop sorting, kept as is

Note for extensions

Components inside the #listing, #toolbar, #bolt--sidebar and #editor roots no longer get this.$store.

Testing

Walkthrough in the admin, with the live Pinia state of each root in the overlay: toolbar colour, sidebar slim toggle across a reload, row size across a reload, row and select-all selection with the bulk count, sidebar toggle leaving the listing selection alone, and the editor booting without a store.

bolt-vuex-to-pinia-walkthrough.mp4

Pinia 2 runs on both Vue 2.7 and Vue 3, so the stores can move ahead of the
Vue 3 upgrade. The stores take the shape they have in the Vue 3 branch: plain
state that components read with mapState and assign directly, with actions
only where there is logic (select/deSelect).

Wherever a component calls a store directly, it passes its own $pinia: the
sidebar, toolbar and listing are separate Vue roots on one page, and outside
setup() and the lifecycle hooks Vue 2.7 has no current instance, so a bare
useStore() would fall back to whichever root booted last.

vue-demi, through which Pinia 2 reaches Vue, ships its Vue 3 build as the
default and only switches to the 2.7 build in its postinstall. webpack and
Vitest resolve it to the 2.7 build directly, so an install that skips scripts
still builds. Both of those are Vue 2 only and go with the move to Vue 3.
The count was tracked separately and decremented on every deSelect, even for
an id that was never selected, so it could drift away from the list. It is
now a getter over the selected ids, and select() no longer adds an id twice.
No editor component reads the editor store (the slim-sidebar flag it held is
driven from the sidebar's own store), so the editor root no longer needs
Pinia. The listing 'type' module was a leftover Vuex module that nothing
imported.
Every other component reads its store state through mapState. The toggler
was the one reading it through useGeneralStore(this.$pinia) in a computed,
with a fallback to false that the boolean state no longer needs.
@Vondry
Vondry force-pushed the refactor/vuex-to-pinia branch from 422ee47 to 9484de6 Compare October 26, 2026 07:26

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant