# vue-page-stack, a navigation stack that keeps Vue pages alive in route order

> hezhongfeng/vue-page-stack is a Vue 3 plugin that sits on top of vue-router and keeps rendered pages in a stack in navigation order, so going back restores the previous page instance and its local state instead of remounting it. It competes with KeepAlive on different terms, and it depends on Vue renderer internals to do it.

**hezhongfeng/vue-page-stack** — Routing and navigation for your Vue SPA. Vue3.0 单页应用导航管理器

- Repository: https://github.com/hezhongfeng/vue-page-stack
- Website: https://vue-page-stack.hezf.online
- Stars: 768 · Forks: 82
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/hezhongfeng-vue-page-stack

## The problem with back navigation in a single page app

vue-page-stack is a Vue 3 single page application navigation stack that preserves route state in stack order, so back navigation feels closer to a native app than a full page remount.

That sentence contains the whole problem. In a Vue Router application, navigating back to a previous route destroys the component instance and mounts a fresh one. If the user had scrolled halfway down a list, typed half a form or expanded an accordion, that state is gone. Native mobile applications behave the other way, keeping the previous screen in memory until you pop it off the stack, which is why web apps often feel worse than their native equivalents for this specific interaction.

The library's approach is to keep the pages in a stack in navigation order rather than in a cache. When you push or forward, the newly rendered page goes on the stack. When you go back or call go with a negative number, the previous pages are restored from the stack, so their local UI state survives. Pages that are no longer reachable after a back navigation are removed from the stack, and replace swaps the current stack entry rather than adding to it.

The events are part of the design rather than an afterthought. The plugin emits back and forward events, which lets a component react to the direction of a transition. That matters for the common case where a forward navigation should reset a form and a back navigation should not.

The browser back and forward buttons work as well, since the stack is driven by the router rather than replacing it.

## How it compares with KeepAlive

The README has a section devoted to KeepAlive, and the differences are precise enough to be worth reading before you choose.

VuePageStack manages pages in navigation order, rather than providing the include, exclude and max options that KeepAlive offers. That is the core difference: KeepAlive is a cache with an eviction policy, while vue-page-stack is a stack.

KeepAlive keeps previously cached pages around until they are evicted. VuePageStack trims pages that are no longer reachable in the stack. In practice that means a page you went back from is discarded rather than sitting in memory waiting for an eviction, which is a better memory profile for deep navigation.

The other difference is the fresh versus restored distinction. Navigating to the same route again creates a fresh page, while returning to an earlier page restores its cached instance. With KeepAlive, navigating to the same route usually reuses the cached component, which is the opposite behaviour and the reason a form in a KeepAlive page sometimes still holds old data.

The practical consequence is that these two are not interchangeable. If you want a specific set of components cached, such as a heavy dashboard that should survive a tab switch, KeepAlive's include list is the better tool and vue-page-stack has no equivalent. If you want the whole history to behave like a native stack, vue-page-stack is the one that does it.

The README also states the principle: the page instance management model is inspired by Vue's own KeepAlive implementation, so the design lineage is Vue's rather than something borrowed from elsewhere.

## Installing it and wiring it into a router view

Installation is a single package:

```js
pnpm install vue-page-stack
```

The plugin is installed with app.use, and the only option is the router instance:

```js
import { createApp } from 'vue';
import { VuePageStackPlugin } from 'vue-page-stack';

const app = createApp(App);

app.use(VuePageStackPlugin, { router });
```

The component goes inside the router-view, wrapping the routed component:

```vue
<template>
  <router-view v-slot="{ Component }">
    <vue-page-stack @back="onBack" @forward="onForward">
      <component :is="Component" :key="$route.fullPath" />
    </vue-page-stack>
  </router-view>
</template>

<script setup>
const onBack = () => {
  console.log('back');
};

const onForward = () => {
  console.log('forward');
};
</script>
```

Three details in that snippet decide whether the library works for you.

The first is the key. The README says to use a stable route key on the rendered route component, and that in most apps $route.fullPath is the safest default because it treats different params and query strings as distinct stack entries. The component uses:is with the key binding, so every distinct URL becomes a distinct stack entry. If your app treats a query string change as the same page, that default will create more entries than you expect.

The second is the slot contract. The notes say the default slot should render a single route component vnode for stack behaviour to apply, and that multiple slot children are passed through unchanged. So the stack logic quietly does nothing if you put two things in the slot.

The third detail is in the same notes and it is the one to plan around: the implementation depends on Vue renderer internals, so upgrades should be validated with the test suite. This is not a stable public API of Vue, and a major Vue upgrade is the moment your build could break.

## The version split and the Vue 2 lineage

The project targets Vue 3, and the Vue 2 line lives on as a separate branch at v1.5.0. That branch pointer is worth noting for anyone who reads this while still on Vue 2, because it tells you the migration path is a version bump rather than a rewrite.

The release history shows the sequence. v3.0.0 shipped in June 2023 as the Vue 3 release, v3.1.0 came in March 2024 and v3.1.5 in July 2024. The last push to the repository was on 2026-06-19.

That gap between the last release and the last commit matters more here than it would for an ordinary library. Because the implementation reaches into Vue renderer internals, the relevant question is not whether vue-page-stack has changed but whether Vue has changed underneath it. A Vue major upgrade is the event that could break the stack behaviour, and the README's own note is that upgrades should be validated with the test suite.

The package is published on npm as vue-page-stack, is MIT licensed, and has a live preview at vue-page-stack.hezf.online with a separate example application repository showing the App.vue wiring.

For a team deciding on this, the practical test is cheap. Install it, wire it into one route, and check whether a back navigation restores the scroll position. If it does, the library earns its place; if the behaviour you need is only about a handful of heavy components, KeepAlive with an include list remains less machinery.

## Development, documentation and the state of the project

The development commands are the standard pnpm set:

```bash
pnpm install
pnpm run build
pnpm run lint
pnpm run test:run
pnpm run test:coverage
```

The README notes something sensible about packaging. The dist/ directory is treated as a release artefact and is generated automatically by npm pack and npm publish through the prepack script, so it does not need to stay checked in during normal development.

Documentation is split in two places. The architecture and maintenance notes are in docs/ARCHITECTURE.md inside the repository, and the release notes are on the GitHub releases page, described as documenting changes release by release.

The project targets Vue 3, and the Vue 2 line is available as a separate branch at v1.5.0, which is worth knowing if you are still on Vue 2 and reading this before upgrading your framework.

The live preview at vue-page-stack.hezf.online and the separate example application repository are both worth opening before you write code, because the difference between a stack entry per query string and a stack entry per route is easier to see in a running demo than in a description.

## Conclusion

Adopt vue-page-stack if your Vue 3 single page app has forms or scroll positions that must survive a back navigation and you want that behaviour without writing keep-alive logic for each route. Skip it if KeepAlive's include and exclude lists already cover your case, since vue-page-stack offers no equivalent control. Verify first by installing it, wiring the vue-page-stack component into your router-view with a key of $route.fullPath, and checking that the browser back button still works and that the back and forward events fire on the transitions you expect.

## FAQ

### What problem does vue-page-stack solve in a Vue 3 app?

It keeps rendered pages in a navigation stack in route order, so going back restores the previous page instance and its local UI state instead of remounting the component and losing scroll position, form input and other local state.

### How does vue-page-stack differ from KeepAlive?

VuePageStack manages pages in navigation order instead of providing the include, exclude and max options of KeepAlive. It trims pages that are no longer reachable in the stack, and navigating to the same route again creates a fresh page while returning to an earlier page restores its cached instance.

### How do I install and use vue-page-stack?

Install with pnpm install vue-page-stack, register the plugin with app.use(VuePageStackPlugin, { router }), and put the vue-page-stack component inside your router-view wrapping the routed component, keyed on $route.fullPath, listening for the back and forward events.

### Which key should I use for the routed component with vue-page-stack?

The README recommends a stable route key, with $route.fullPath as the safest default in most apps, because it treats different params and query strings as distinct stack entries. If your app treats a query string change as the same page, that default creates more stack entries than you expect.

### What are the risks of using vue-page-stack?

The implementation depends on Vue renderer internals, so the README says upgrades should be validated with the test suite. There is also no include or exclude equivalent to KeepAlive, so you cannot select which pages participate in the stack.

## Sources

- [hezhongfeng/vue-page-stack on GitHub](https://github.com/hezhongfeng/vue-page-stack)
- [License: MIT](https://github.com/hezhongfeng/vue-page-stack/blob/master/LICENSE)
- [Project website](https://vue-page-stack.hezf.online)
- [README](https://github.com/hezhongfeng/vue-page-stack/blob/master/README.md)
- [Releases](https://github.com/hezhongfeng/vue-page-stack/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hezhongfeng-vue-page-stack
