Skip to content

Conversation

@cernymatej
Copy link
Member

🔗 Linked issue

fix #32898

📚 Description

This should fix an issue with syncing the route object during navigation between 2 child routes.

@cernymatej cernymatej requested a review from danielroe as a code owner August 8, 2025 14:38
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 8, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@32899

nuxt

npm i https://pkg.pr.new/nuxt@32899

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@32899

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@32899

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@32899

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@32899

commit: 24bdf01

@coderabbitai
Copy link

coderabbitai bot commented Aug 8, 2025

Walkthrough

  • In packages/nuxt/src/pages/runtime/plugins/router.ts, the afterEach hook condition for calling syncCurrentRoute() was changed to compare the default component of the last matched route record between the to and from routes instead of the first.
  • In test/nuxt/composables.test.ts, a new async test was added to verify route synchronisation after a child suspense resolves. It imports NuxtPage, dynamically registers nested routes (/parent with children), mounts a suspended component rendering NuxtPage, navigates between /parent and /parent/suspense, asserts HTML and route name before and after suspense resolution, and clears routes after each test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/nuxt/src/pages/runtime/plugins/router.ts (2)

115-121: Minor readability refactor: avoid repeated indexing

Extract leaf components into locals to reduce repetition and improve clarity.

-      if (to.matched[to.matched.length - 1]?.components?.default === from.matched[from.matched.length - 1]?.components?.default) {
-        syncCurrentRoute()
-      }
+      const toLeaf = to.matched[to.matched.length - 1]?.components?.default
+      const fromLeaf = from.matched[from.matched.length - 1]?.components?.default
+      if (toLeaf === fromLeaf) {
+        syncCurrentRoute()
+      }

115-121: Document the nuance for future maintainers

Add a brief comment noting that we compare leaf components so that manual sync only occurs when the page component is reused (e.g., param/query change), and not during child-to-child transitions that should suspend.

Example:

// Compare leaf (last) matched components. If the page component is reused (e.g. param change),
// no suspense is triggered, so we manually sync the route. For child→child transitions with a new
// leaf component, suspense will resolve and update the route via `page:finish`.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ea3fc3 and 9a8730a.

📒 Files selected for processing (1)
  • packages/nuxt/src/pages/runtime/plugins/router.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/pages/runtime/plugins/router.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.

Applied to files:

  • packages/nuxt/src/pages/runtime/plugins/router.ts
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/pages/runtime/plugins/router.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build
  • GitHub Check: codeql (javascript-typescript)
  • GitHub Check: code
🔇 Additional comments (2)
packages/nuxt/src/pages/runtime/plugins/router.ts (2)

118-120: Correct fix: compare leaf (last) matched components to avoid premature sync

Comparing the last matched records aligns with child-to-child navigation semantics and prevents early route sync before suspense resolves. This should address #32898 as intended.


115-121: No other direct matched[0] checks found
All occurrences of component-equality checks use either the last matched record (in router.afterEach) or an every() check across all matches (in packages/nuxt/src/app/components/utils.ts), so there’s no remaining logic relying solely on matched[0].

@danielroe
Copy link
Member

danielroe commented Aug 8, 2025

do you think you could add a unit/runtime test? 🙏

@codspeed-hq
Copy link

codspeed-hq bot commented Aug 8, 2025

CodSpeed Performance Report

Merging #32899 will not alter performance

Comparing cernymatej:fix/route-sync-in-child-routes (24bdf01) with main (c0ff140)

Summary

✅ 10 untouched benchmarks

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/fixtures/basic/app/pages/parent/suspense.vue (1)

8-8: Optional: increase delay slightly to reduce flakiness

A 1ms timeout can be racy on CI. Consider a small bump (e.g. 10ms) to make the suspense resolution timing more robust:

-await new Promise(resolve => setTimeout(resolve, 1))
+await new Promise(resolve => setTimeout(resolve, 10))
test/nuxt/composables.test.ts (2)

475-477: Optional: pre-wire the page:finish hook to avoid any race

It’s safer to set up the page:finish promise before calling navigateTo, to eliminate any timing edge cases:

-function waitForPageChange () {
-  return new Promise<void>(resolve => nuxtApp.hooks.hookOnce('page:finish', () => resolve()))
-}
+function nextPageFinish () {
+  return new Promise<void>(resolve => nuxtApp.hooks.hookOnce('page:finish', resolve))
+}

And use:

const wait = nextPageFinish()
await navigateTo('/parent')
await wait

If you prefer to keep the current flow, please verify it still passes consistently on CI once the SFC/tsconfig issue is resolved.


497-533: Test logic LGTM; add wrapper unmount to prevent leaks

The assertions correctly validate that:

  • Before suspense resolves, the parent route/component remains active.
  • After page:finish, the child suspense route is active and route.name updates.

Add unmount to avoid lingering instances across tests:

-const el = await mountSuspended({ setup: () => () => h(NuxtPage) })
-const route = useRoute()
+const el = await mountSuspended({ setup: () => () => h(NuxtPage) })
+const route = useRoute()
+try {
   await navigateTo('/parent')
   await waitForPageChange()
 
   expect(el.html()).toContain('<div> parent/index </div>')
   expect(route.name).toBe('parent')
 
   await navigateTo('/parent/suspense')
 
   expect(el.html()).toContain('<div> parent/index </div>')
   expect(route.name).toBe('parent')
 
   await waitForPageChange()
 
   expect(el.html()).toContain('<div> parent/suspense </div>')
   expect(route.name).toBe('parent-suspense')
+} finally {
+  el.unmount()
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8730a and 3a38ad0.

📒 Files selected for processing (2)
  • test/fixtures/basic/app/pages/parent/suspense.vue (1 hunks)
  • test/nuxt/composables.test.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.vue

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Use <script setup lang="ts"> and the composition API when creating Vue components

Files:

  • test/fixtures/basic/app/pages/parent/suspense.vue
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • test/nuxt/composables.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/nuxt/composables.test.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
Learnt from: Tofandel
PR: nuxt/nuxt#0
File: :0-0
Timestamp: 2024-11-11T12:34:22.648Z
Learning: Ensure that AI-generated summaries accurately reflect the key changes in the PR, focusing on notable changes such as the removal of unused imports and variables starting with underscores.
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/e2e/**/*.{ts,js} : Write end-to-end tests using Playwright and `nuxt/test-utils`
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.vue : Use `<script setup lang="ts">` and the composition API when creating Vue components

Applied to files:

  • test/fixtures/basic/app/pages/parent/suspense.vue
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.

Applied to files:

  • test/fixtures/basic/app/pages/parent/suspense.vue
  • test/nuxt/composables.test.ts
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/e2e/**/*.{ts,js} : Write end-to-end tests using Playwright and `nuxt/test-utils`

Applied to files:

  • test/fixtures/basic/app/pages/parent/suspense.vue
  • test/nuxt/composables.test.ts
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Write unit tests for core functionality using `vitest`

Applied to files:

  • test/nuxt/composables.test.ts
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • test/nuxt/composables.test.ts
🪛 GitHub Actions: autofix.ci
test/fixtures/basic/app/pages/parent/suspense.vue

[error] 1-1: Vite plugin 'vite:vue' error caused by missing tsconfig.server.json file during test execution.

test/nuxt/composables.test.ts

[error] 1-1: Test failure in routing utilities: useRoute > should sync route after child suspense resolves. Parsing failed for '/home/runner/work/nuxt/nuxt/test/fixtures/basic/.nuxt/tsconfig.server.json': ENOENT: no such file or directory.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
test/fixtures/basic/app/pages/parent/suspense.vue (1)

1-5: Template looks good for the fixture

The minimal markup is fine and fits the test’s intent.

test/nuxt/composables.test.ts (2)

25-25: Importing NuxtPage is appropriate

Using NuxtPage from #components is the right way to render current route in this test.


472-482: Good test hygiene clearing dynamic routes after each

Defining waitForPageChange() and calling router.clearRoutes() in afterEach avoids test leakage. Looks good.

Comment on lines 7 to 9
<script setup lang="ts">
await new Promise(resolve => setTimeout(resolve, 1))
</script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix CI: avoid dependency on missing .nuxt/tsconfig.server.json (remove TS) or commit minimal tsconfig

The autofix.ci pipeline fails because Vite’s vue plugin tries to read test/fixtures/basic/.nuxt/tsconfig.server.json when parsing a TS SFC, which isn’t present in CI. Since this fixture doesn’t use TypeScript features, the simplest fix is to drop lang="ts".

Apply this diff:

-<script setup lang="ts">
+<script setup>
 await new Promise(resolve => setTimeout(resolve, 1))
 </script>

Alternative (keep TS): commit a minimal test/fixtures/basic/.nuxt/tsconfig.server.json to satisfy the plugin.
Example content:

{
  "compilerOptions": {
    "allowJs": true,
    "skipLibCheck": true,
    "types": ["node", "vite/client"]
  }
}

To confirm the root cause and the fix locally/CI:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Check presence of tsconfig.server.json in fixture:"
if [ -f test/fixtures/basic/.nuxt/tsconfig.server.json ]; then
  echo "Found: test/fixtures/basic/.nuxt/tsconfig.server.json"
else
  echo "Missing: test/fixtures/basic/.nuxt/tsconfig.server.json"
fi

echo
echo "Check if the fixture uses TypeScript in the SFC:"
rg -n 'script setup.*lang="ts"' test/fixtures/basic/app/pages/parent/suspense.vue || true

Length of output: 537


Fix CI failure in basic fixture: drop lang="ts"

Vite’s Vue plugin is attempting to read a missing test/fixtures/basic/.nuxt/tsconfig.server.json when parsing this TS SFC, causing the autofix.ci pipeline to fail. Since this component doesn’t actually use any TypeScript-specific features, removing the lang="ts" attribute resolves the issue.

Please apply the following change in test/fixtures/basic/app/pages/parent/suspense.vue:

--- a/test/fixtures/basic/app/pages/parent/suspense.vue
+++ b/test/fixtures/basic/app/pages/parent/suspense.vue
@@ Lines 7-9
-<script setup lang="ts">
+<script setup>
 await new Promise(resolve => setTimeout(resolve, 1))
 </script>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script setup lang="ts">
await new Promise(resolve => setTimeout(resolve, 1))
</script>
<script setup>
await new Promise(resolve => setTimeout(resolve, 1))
</script>
🤖 Prompt for AI Agents
In test/fixtures/basic/app/pages/parent/suspense.vue around lines 7 to 9, remove
the lang="ts" attribute from the <script setup> tag because the file does not
use TypeScript-specific features and having lang="ts" causes Vite’s Vue plugin
to look for a missing tsconfig.server.json, leading to CI failures. Change
<script setup lang="ts"> to just <script setup>.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
test/nuxt/composables.test.ts (3)

508-519: Optional: avoid runtime template compilation in tests

Relying on template strings in defineComponent can depend on the runtime-compiler build. To keep tests robust across environments, prefer render functions and an async setup to simulate suspense.

You can replace both child components as follows:

-          component: defineComponent({
-            template: '<div> parent/index </div>',
-          }),
+          component: defineComponent({
+            setup: () => () => h('div', ' parent/index ')
+          }),
...
-          component: defineComponent({
-            template: '<div> parent/suspense </div>',
-            setup: () => new Promise(resolve => setTimeout(resolve, 1)),
-          }),
+          component: defineComponent({
+            async setup () {
+              await new Promise(resolve => setTimeout(resolve, 1))
+              return () => h('div', ' parent/suspense ')
+            },
+          }),

542-544: Deduplicate route cleanup

You already call router.clearRoutes() in afterEach. Removing the explicit removeRoute avoids redundant cleanup.

Apply:

-    el.unmount()
-    router.removeRoute('parent-test')
+    el.unmount()

479-481: router.clearRoutes() fully clears all routes—ensure each test sets up its own baseline

It turns out clearRoutes() doesn’t reset to a built-in default or “fixture” set of routes, but empties the router entirely. In this suite the first test relies on the default catch-all route before cleanup, and subsequent tests add their own routes manually, so it works today—but it’s brittle if tests are reordered or if new tests expect the default routes.

Consider one of the following to make the behaviour explicit and robust:

• Move router.clearRoutes() into a beforeEach, then explicitly add only the routes needed for that test (including the default catch-all if required).
• Or drop the blanket clearRoutes() and use router.removeRoute(name) in each test’s teardown to remove only the routes that were added.
• If you need a true “reset to baseline,” implement or invoke a helper that re-adds the default routes after clearing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a38ad0 and 24bdf01.

📒 Files selected for processing (1)
  • test/nuxt/composables.test.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • test/nuxt/composables.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/nuxt/composables.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.

Applied to files:

  • test/nuxt/composables.test.ts
🧬 Code Graph Analysis (1)
test/nuxt/composables.test.ts (4)
packages/nuxt/src/app/nuxt.ts (1)
  • useNuxtApp (544-557)
packages/nuxt/src/app/composables/index.ts (3)
  • useRouter (15-15)
  • useRoute (15-15)
  • navigateTo (15-15)
packages/nuxt/src/app/composables/router.ts (3)
  • useRouter (15-17)
  • useRoute (20-28)
  • navigateTo (119-224)
packages/schema/src/types/hooks.ts (1)
  • NuxtPage (28-50)
🔇 Additional comments (5)
test/nuxt/composables.test.ts (5)

25-25: Importing NuxtPage from #components is appropriate

This is the right component to render nested route children in tests. No issues.


472-474: Initialising nuxtApp and router at describe scope is fine

Consistent with the rest of the suite and avoids repeated lookups.


475-477: Good choice of synchronisation point with page:finish

Waiting on page:finish precisely targets post-suspense completion, matching the bug’s intent and the router change comparing last matched records.


497-521: Test setup accurately reproduces the nested child + suspense scenario

The custom parent route with two children (one async) mirrors the reported bug and exercises the fix. Nice use of NuxtPage to render the active child.


523-541: Assertions correctly validate pre- and post-suspense route sync

  • Before suspense resolves: DOM still shows the previous child; route.name remains 'parent'.
  • After page:finish: DOM updates to the async child; route.name updates to 'parent-suspense'.

This ensures the Nuxt route object reflects the final matched routes only after suspense resolution, which aligns with the fix.

@danielroe danielroe merged commit c0e5065 into nuxt:main Aug 19, 2025
46 checks passed
@github-actions github-actions bot mentioned this pull request Aug 19, 2025
@github-actions github-actions bot mentioned this pull request Sep 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

route sync happens before suspense resolution when navigating between child routes

2 participants