DEV Community

Cover image for Zombie State in Pinia: A Silent Bug You Might Already Have
hichem ben chaabene
hichem ben chaabene

Posted on

Zombie State in Pinia: A Silent Bug You Might Already Have

Pinia is one of the best state management libraries in the Vue ecosystem.
It’s simple, type-safe, and feels natural with the Composition API.

But there’s a subtle problem many teams run into — often without realizing it.

That problem is zombie state.

What Is Zombie State?

Zombie state is state that should no longer exist, but is still alive and affecting your app.

It usually comes from:

  • A previous page
  • An old user flow
  • A completed form
  • A finished async request

Yet it continues to:

  • Prefill inputs
  • Trigger validation
  • Affect UI decisions
  • Cause "random" bugs

It doesn’t crash your app.
It just quietly causes confusion.

Why Zombie State Happens in Pinia

Pinia stores are:

  • Global
  • Long-lived
  • Singletons by default

This means one important thing:

Navigation away doesn't reset store data

Unless you explicitly reset it, the state stays in memory — even when it no longer makes sense.

A Very Common Example

// useWizardStore.ts
export const useWizardStore = defineStore('wizard', () => {
  const step = ref(2)
  const email = ref('old@email.com')

  return { step, email }
})
Enter fullscreen mode Exit fullscreen mode

What happens

1- user opens the wizard
2- fills some steps
3- navigates away
4- comes back later

The wizard:

  • Starts on step 2
  • Shows the old email
  • Fails validation unexpectedly

The store never reset, that's the zombie state.

Async Zombie State (Even Trickier)

Zombie state can also come from async code.

async function loadUser() {
  user.value = await fetchUser()
}
Enter fullscreen mode Exit fullscreen mode

Scenario:
1- User opens page A
2- Request starts
3- User navigates to page B
4- Request from Page A finishes late

Result:
Page B shows data from page A

This is zombie state caused by a late async response.

How Zombie State Shows Up in Real Apps

You might recognize these symptoms:

  • Forms already filled when they shouldn’t be
  • Validation errors on first render
  • Wrong data after navigation
  • UI behaving differently after back/forward
  • Bugs that disappear on refresh

These are some of the hardest bugs to debug — because they depend on history, not just current state.

The Key Question to Ask

Whenever you create a store, ask:

Who owns this state, and when should it be destroyed?

If the answer isn’t clear, zombie state is likely.

How to Prevent Zombie State in Pinia

1- Add an explicit reset

The simplest and most effective solution

export const useWizardStore = defineStore('wizard', () => {
  const step = ref(1)
  const email = ref('')

  function reset() {
    step.value = 1
    email.value = ''
  }

  return { step, email, reset }
})
Enter fullscreen mode Exit fullscreen mode

Use it when the flow ends

onUnmounted(() => store.reset())
Enter fullscreen mode Exit fullscreen mode

Pinia doesn’t reset state for you — you have to be intentional.

2- Reset on context changes
if state depends on route parameters, reset when they change.

watch(
  () => route.params.id,
  () => store.reset()
)
Enter fullscreen mode Exit fullscreen mode

This prevents old data from leaking into new contexts.

3- Guard Async Requests

A small guard can prevent async zombie state.

let requestId = 0

async function load() {
  const id = ++requestId
  const data = await fetchData()

  if (id !== requestId) return
  state.value = data
}
Enter fullscreen mode Exit fullscreen mode

Late responses are ignored instead of overwriting valid state.

4- Separate UI State from Domain State

UI state often becomes zombie state.

Good candidates for stores

-User data
-Orders
-Permissions

Bad candidates
-Input values
-Modal visibility
-Hover or focus state

Keep short-lived UI state close to the component when possible.

Zombie State vs Stale State

They’re not the same.

Type Meaning
Stale state Old but still valid
Zombie state Invalid but still active

Zombie state is dangerous because it looks correct — until it isn’t.

A Simple Rule of Thumb

If state outlives its owner, it becomes a zombie.

Once you start thinking about state ownership and lifecycle, many “random” bugs suddenly make sense.

Final Thoughts

Zombie state isn’t a Pinia problem.
It’s a state lifecycle problem.

Pinia gives us powerful tools — but with that power comes responsibility:

  • Define ownership
  • Control lifetime
  • Reset intentionally

Doing this consistently leads to apps that are:

  • Easier to reason about
  • Easier to debug
  • More predictable for users

If this helps even one developer avoid a hard-to-trace production bug, it’s worth sharing.

Top comments (0)