Learn Pinia - DevTools & Debugging
Episode 10 of 23

Learn Pinia - DevTools & Debugging

Pinia integrates fully with Vue DevTools. This episode covers how to inspect state and actions per store, the time-travel feature, and troubleshooting common problems: non-reactive state caused by destructuring, undetected changes, and circular store dependencies.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Debugging state management without DevTools is like finding a needle in a haystack. Fortunately, Pinia integrates fully with Vue DevTools — every store, state, getter, and action shows up there, complete with the time-travel feature for replaying changes.

Episode 10 covers two things: getting the most out of Vue DevTools, and troubleshooting the most common problems Pinia developers face. After this episode, you'll have a systematic debugging workflow.

Vue DevTools and Pinia

Vue DevTools shows a separate Pinia tab for all the app's stores. Inside it you can see:

  • State: the current value of every field, including real-time changes.
  • Getters: the results of derived state calculations.
  • Actions: the list of action calls with their arguments and results.
  • Dependencies: which components use this store.

Make sure the Vue DevTools extension is active and the project is running in development mode. If the Pinia tab doesn't appear, restart the app after Pinia is installed in main.ts.

Time-Travel

DevTools' flagship feature is time-travel — replaying the sequence of state mutations:

Access the DevTools panel
npm run dev

While the app is running, open the DevTools panel, the Pinia tab, and run a few actions. In the mutation list section, you can click an earlier point in time — the entire state will return to its state at that moment. This is very useful for finding which action caused a bug.

Tracking Action Calls

Besides DevTools, Pinia provides the $onAction API to listen to every action call from within code. This is useful when a bug only appears in a specific environment and you want to record its trace:

JSSubscribe to action calls
store.$onAction(({ name, args, after, onError }) => {
  console.log(`action ${name} called`, args)
  after((result) => {
    console.log(`${name} finished`, result)
  })
  onError((error) => {
    console.error(`${name} failed`, error)
  })
})

store.$onAction({...}) receives a callback with information about the action name, arguments, the after hook, and the onError hook. If this pattern reminds you of $subscribe in episode 13, you guessed right — $subscribe monitors state changes, while $onAction monitors store method calls.

Troubleshooting: Non-Reactive State

The most common problem in Pinia: the UI never changes even though the state was changed. The cause is almost always ordinary destructuring:

JSCause of non-reactive state
// WRONG: copies the value, not a reactive reference
const { count } = useCounterStore()
 
// RIGHT: take refs that stay connected
const { count } = storeToRefs(useCounterStore())

const { count } = useCounterStore() just copies the number — changes in the store won't be seen. Replace it with storeToRefs(useCounterStore()) so count becomes a live ref.

Troubleshooting: Undetected Changes

If an assignment like store.items.push(...) doesn't trigger a re-render, check whether the object being written is truly reactive state, not a copy:

JSUndetected changes
// WRONG: a new array outside the store
const baru = [...store.items, item]
store.items = baru
 
// RIGHT: mutate directly with a patch function
store.$patch((state) => {
  state.items.push(item)
})

store.$patch((state) => state.items.push(item)) ensures the mutation happens inside reactive state, so DevTools and subscribers detect it too.

Troubleshooting: Circular Store Dependency

Two stores that use each other can cause errors in DevTools:

JSPattern that triggers a circular dependency
// stores/a.ts uses useBStore, stores/b.ts uses useAStore

Pinia usually handles this circularity automatically, but it can become a problem in getters. The solution: move the combined logic into one of the stores only, or into a separate composable, so dependencies flow one way.

Tip

When debugging, give actions descriptive names — for example addItem instead of set. These action names are what appear in the time-travel timeline, so clear names speed up investigation.

Closing

Episode 10 equips you with systematic debugging skills. You can now inspect stores in Vue DevTools, use time-travel to find the cause of bugs, and handle the three common problems: non-reactive state, undetected changes, and circular dependencies.

Key takeaways:

  • The Pinia tab in Vue DevTools shows state, getters, actions, and dependencies.
  • Time-travel replays mutations to find the action that caused a bug.
  • Non-reactive state is usually caused by destructuring without storeToRefs.
  • Mutate arrays with the function form of $patch so they're detected.
  • Circular dependencies are solved with one-way dependency flow.
  • Give actions descriptive names to make debugging easier.

In the next episode, episode 11, we'll discuss SSR and Nuxt integration — creating a per-request Pinia instance on the server, using the @pinia/nuxt module for auto-setup, and handling hydration state to avoid mismatches. This opens Pinia up to server-rendered applications.