Skip to content

fix(compactRoutes): key NuxtPage by interpolated pattern, not full path#4014

Open
Togetic wants to merge 1 commit into
nuxt-modules:mainfrom
Togetic:fix/compact-routes-nuxtpage-key
Open

fix(compactRoutes): key NuxtPage by interpolated pattern, not full path#4014
Togetic wants to merge 1 commit into
nuxt-modules:mainfrom
Togetic:fix/compact-routes-nuxtpage-key

Conversation

@Togetic

@Togetic Togetic commented Jun 28, 2026

Copy link
Copy Markdown

🔗 Linked issue

Fixes #4001

📚 Description

With experimental.compactRoutes, the route-locale-detect plugin keys compact routes by the full resolved path:

route.meta.key = (r) => r.path

Nuxt uses meta.key as the <NuxtPage> component key, so the key changes on every child navigation (e.g. /de/foo/bar/de/foo/baz). Vue then treats the parent record as a different instance and remounts the whole nested subtree — state held in a parent component across child navigation is wiped, and onBeforeMount / onBeforeUnmount re-run unexpectedly.

This only reproduces on non-default locales: the default-locale tree isn't compact (no meta.key), so Nuxt falls back to the record's own interpolated pattern, which is stable across child navigation.

The upstream intent — remount when the locale param actually changes, e.g. /de/x/fr/x (introduced in #3957) — is legitimate, but keying by the full path over-fires on every child navigation.

Fix: key by the record's own interpolated pattern instead, matching Nuxt's default <NuxtPage> keying:

const pattern = route.path.replace(/(:\w+)\([^)]+\)/g, '$1').replace(/(:\w+)[?+*]/g, '$1')
route.meta.key = (r) => pattern.replace(/:\w+/g, m => r.params[m.slice(1)]?.toString() || '')

For a compact record /:locale(en|de|fr)/foo, the key resolves to /de/foo for any child of foo under /de — stable across /de/foo/bar/de/foo/baz, while still changing to /fr/foo on a locale switch, so the intended locale-change remount is preserved.

✅ Verification

  • pnpm test:types, pnpm lint, and pnpm test:unit all pass.
  • Verified manually on a nested-pages app: child navigation no longer remounts the parent; a locale switch (/de/x/fr/x) still remounts as intended.

📝 Note on tests

compactRoutes is currently covered only at the route-table level (test/pages/route_localization.test.ts); there's no e2e fixture exercising compact-route runtime navigation, and meta.key is assigned inside the runtime plugin. I'm happy to add a dedicated fixture + spec (nested pages asserting the parent stays mounted across child navigation and remounts on a locale switch) if you'd like that folded into this PR.

Summary by CodeRabbit

  • Bug Fixes
    • Improved locale-aware navigation so nested pages stay mounted when moving between child routes.
    • Routes now refresh only when the locale-related route value actually changes, reducing unnecessary remounts and page resets.

With `experimental.compactRoutes`, the route-locale-detect plugin set
`meta.key` to the full resolved path, so the `<NuxtPage>` key changed on
every child navigation and Vue remounted the whole nested subtree —
parent state was lost and `onBeforeMount`/`onBeforeUnmount` re-fired —
on non-default locales only (the default-locale tree isn't compact, so
Nuxt's stable interpolated key was used there).

Key by the record's own interpolated pattern instead, matching Nuxt's
default `<NuxtPage>` keying. Nested parents now stay mounted across child
navigation and still remount when the locale param actually changes
(e.g. `/de/x` -> `/fr/x`), preserving the original intent from nuxt-modules#3957.

Fixes nuxt-modules#4001
@Togetic Togetic requested a review from BobbieGoede as a code owner June 28, 2026 09:47
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 43fba846-e01f-4bf4-acf9-4e154186260d

📥 Commits

Reviewing files that changed from the base of the PR and between bd54cb9 and b310be9.

📒 Files selected for processing (1)
  • src/runtime/plugins/route-locale-detect.ts

Walkthrough

In route-locale-detect.ts, the computation of route.meta.key for compact routes is updated. Previously, the key was derived from the resolved record's path. Now it is computed by normalizing route.path (stripping parameter modifiers) and replacing each :param placeholder with the corresponding r.params[param]?.toString() value, falling back to an empty string when the parameter is absent.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: compactRoutes now keys NuxtPage by interpolated pattern instead of full path.
Linked Issues check ✅ Passed The change addresses #4001 by keeping compact-route keys stable across child navigation while still changing on locale param switches.
Out of Scope Changes check ✅ Passed The patch is narrowly scoped to route-locale-detect key generation and does not introduce unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

compactRoutes: <NuxtPage> key is the full path, remounting nested pages on every child navigation (non-default locales only)

1 participant