Keep Your UI State Alive in Inertia.js: How preserveState Works and When to Use It
When navigating within an Inertia SPA, you can keep the current component instance alive with preserveState. Learn how it works, verify it, and when to avoid it.
09 Dec 2025, 12:12 UTC

Problem: Losing UI State on Client‑Side Navigation
In a typical Inertia.js single‑page application (SPA), clicking a link or submitting a form triggers a client‑side navigation that replaces the current page component. The framework then mounts a fresh instance of the target component. While this is efficient for server‑side data, it wipes out any local, reactive state—such as form inputs, toggle switches, or temporary counters—that the user may have interacted with before navigating away.
For many user flows, that loss feels jarring. Consider a user filling out a multi‑step form: if they click “Back” to review previous steps, the form fields should retain what they typed. A naive SPA that always remounts will reset those fields to their initial values, forcing the user to re‑enter data.
What is Inertia’s preserveState?
The preserveState option tells Inertia to keep the current component instance alive when navigating to the same component type. Instead of discarding the vnode and its reactive data, Inertia caches it. On the next navigation to that component, the cached instance is reused, preserving any local state that was already set.
Usage is simple:
import { InertiaLink } from '@inertiajs/vue3';
<InertiaLink href="/profile" :preserveState="true">Back</InertiaLink>
or programmatically:
import { Inertia } from '@inertiajs/inertia';
Inertia.visit('/profile', { preserveState: true });
Only navigations performed by Inertia (via InertiaLink or Inertia.visit) trigger state preservation. A full page reload always discards the component instance.
How Does It Work Under the Hood?
When Inertia receives a navigation request, it checks the target component’s name. If that name matches an entry already in its internal cache, it re‑uses the cached vnode and the associated reactive data. The component’s setup function is not re‑executed; the existing reactive object stays in memory.
Because the component instance is kept alive, any watchers, event listeners, or timers that were attached remain active. That’s why you must clean up in beforeUnmount if you start side‑effects that could linger.
Practical Example: A Counter Component
Below is a minimal Vue 3 component that demonstrates preserveState. The component displays a counter that the user can increment. When the user navigates back to the same component via an InertiaLink with preserveState: true, the counter value stays where it was.
/* Counter.vue */
<div>
<p>Counter: {{ count }}</p>
<button @click="count++">Increment</button>
<InertiaLink href="/counter" :preserveState="true">Go to Counter</InertiaLink>
</div>
Steps to verify:
- Open the component in the browser.
- Click the
Incrementbutton twice; the counter shows2. - Click the
Go to Counterlink. - Observe that the counter remains at
2instead of resetting to0.
To confirm that the same instance is reused, open Chrome DevTools and inspect the component in the Components panel. The component’s __v id should stay the same between navigations.
Memory verification:
- Open the Memory tab.
- Take a snapshot before navigation.
- Navigate with
preserveState: trueand take another snapshot. - The number of
Counterinstances should not increase; only the cached instance should remain.
Trade‑offs & Limitations
| Aspect | Benefit | Risk |
|---|---|---|
| Performance | Reduced mount overhead, faster perceived navigation. | None significant. |
| Memory Usage | Component stays in memory, potentially reducing GC cycles. | Long‑lived instances can bloat memory if many components are preserved. |
| Stale Server‑Side Data | Local state survives, keeping UI in sync with user actions. | Props fetched from the server may become out of date; the component will still display old props unless a full visit is forced. |
| Side‑Effect Leakage | Event listeners or timers persist. | Can cause memory leaks if not cleaned up in beforeUnmount. |
Because preserveState only applies to client‑side navigation, a hard refresh or a navigation to a different domain will always discard the cached instance. If your component relies heavily on server‑side props, you may need to force a full visit (omit preserveState) to ensure fresh data.
When to Use It (and When to Avoid It)
- Use it when: you have transient UI state that should survive navigation, such as form drafts, modal visibility, or unsaved scroll positions.
- Avoid it when: the component should always re‑initialize with fresh data, like a dashboard that displays real‑time metrics or a page that depends on URL parameters that change.
- Consider combining
preserveStatewithpreserveScrollto keep the user’s scroll position across navigations. - If you need to keep the component alive for a long time but also want to free memory when it’s no longer needed, call
Inertia.clearCache()after the component is unmounted.
Actionable Takeaways
1. Add preserveState: true to any Inertia link or visit that should keep local UI state.
2. Verify instance reuse in DevTools and memory snapshots. This ensures you’re not unintentionally creating new instances.
3. Clean up side‑effects in beforeUnmount. Prevent memory leaks when the component eventually gets unmounted.
4. Monitor server‑side prop freshness. If the component depends on data that changes on the server, consider forcing a full visit or re‑fetching data in onMounted when the component is reused.
5. Use Inertia.clearCache() judiciously. When you need to free memory for components that are no longer in the UI, call this method from a parent component or a global event listener.
By understanding how preserveState works and its trade‑offs, you can keep your SPA’s UI fluid without sacrificing performance or correctness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.