chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(nuxt): Change default routing strategy to path-based - #7260

Merged
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path
Nov 21, 2025
Merged

chore(nuxt): Change default routing strategy to path-based#7260
wobsoriano merged 23 commits into
vincent-and-the-doctorfrom
rob/nuxt-routing-path

Conversation

@wobsoriano

@wobsorianowobsoriano commented Nov 19, 2025

Copy link
Copy Markdown
Member

Description

Changes the default routing strategy for Clerk UI components in the Nuxt SDK from hash-based to path-based routing, matching the behavior of other fullstack SDKs like Next.js, React Router, and TanStack Start.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added support for both path and hash-based routing strategies for authentication and organization components.
  • Chores

    • Updated default routing strategy to path-based routing for SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, and OrganizationList components.
    • Updated authenticated user redirect flow to direct to '/user-profile'.
    • Bumped @clerk/nuxt to major version and @clerk/vue to minor version.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercelBot commented Nov 19, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewCommentsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewCommentNov 21, 2025 4:23pm

@changeset-bot

changeset-botBot commented Nov 19, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4cc101c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
NameType
@clerk/nuxtMajor
@clerk/vueMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces path-based routing as the default strategy for major UI components in @clerk/nuxt via a new routing configuration composable in @clerk/vue. It adds wrapped component implementations in Nuxt that automatically compute and inject routing props, updates integration templates to support both path and hash routing, and adjusts tests accordingly.

Changes

Cohort / File(s)Summary
Version and configuration management
.changeset/thirty-cherries-pull.md, .changeset/wild-bees-explode.md, pnpm-workspace.yaml, packages/vue/package.json, packages/nuxt/package.json
Version bump documentation for @clerk/nuxt (major) and @clerk/vue (minor); Vue catalog entry added to workspace; vue devDependency added to @clerk/nuxt
Vue routing composable and error messages
packages/vue/src/composables/useRoutingProps.ts, packages/vue/src/composables/__tests__/useRoutingProps.test.ts, packages/vue/src/errors/messages.ts, packages/vue/src/internal.ts, packages/vue/tsup.config.ts
New composable for merging component props with routing configuration; path validation and error handling for routing modes; comprehensive test coverage; internal export added
Nuxt wrapped components
packages/nuxt/src/runtime/components/uiComponents.ts, packages/nuxt/src/runtime/components/index.ts, packages/nuxt/src/module.ts, packages/nuxt/tsup.config.ts
New wrapper components (SignIn, SignUp, UserProfile, OrganizationProfile, CreateOrganization, OrganizationList) that derive path-aware routing props; re-export structure updated; build configuration adjusted with glob patterns and Vue external dependency
Integration template updates
integration/templates/nuxt-node/app/middleware/auth.global.js, integration/templates/nuxt-node/app/pages/sign-in.vue, integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue, integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue, integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue
Path-based routing strategy implemented with catch-all route parameters; new hash routing example added; middleware updated to match new route patterns and redirect authenticated users to /user-profile
Integration tests
integration/tests/nuxt/basic.test.ts, integration/tests/nuxt/navigation.test.ts
Route navigation updated from /user to /user-profile; hash routing test removed from basic suite; new comprehensive navigation test suite added covering path/hash routing flows and profile page routing

Sequence Diagram

sequenceDiagram
participant App as Nuxt App
participant Wrapper as Wrapped Component<br/>(SignIn/SignUp/etc)
participant Composable as useRoutingProps
participant BaseComponent as Base Clerk<br/>Vue Component
App->>Wrapper: Mount (with optional path prop)
activate Wrapper
Wrapper->>Composable: useRoutingProps(componentName, props, routingOptions)
activate Composable
Composable->>Composable: Validate path with routing mode
alt routing === 'path'
Composable->>Composable: Require path prop (error if missing)
else routing !== 'path' (hash/virtual)
Composable->>Composable: Reject path prop if provided (error)
Composable->>Composable: Set routing mode explicitly
end
Composable-->>Wrapper: Return computed routing props
deactivate Composable
Wrapper->>Wrapper: usePathnameWithoutSplatRouteParams()<br/>to derive current path
Wrapper->>BaseComponent: Render with merged routing props
activate BaseComponent
BaseComponent-->>Wrapper: Rendered component
deactivate BaseComponent
Wrapper-->>App: Rendered UI with path-aware routing
deactivate Wrapper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • packages/nuxt/src/runtime/components/uiComponents.ts requires careful review of the wrapper logic, path derivation, and generic component structure with augmented Page/Link properties
  • packages/vue/src/composables/useRoutingProps.ts implements validation logic that enforces routing mode constraints; verify error conditions and precedence rules align with intended behavior
  • packages/nuxt/src/module.ts changes component registration strategy; ensure wrapped and standard component separation is correct
  • integration/templates/nuxt-node/app/middleware/auth.global.js regex-based route matching and redirect logic updates should be validated for edge cases
  • Test coverage additions (navigation.test.ts) validate new routing patterns; verify hash vs. path routing assertions are comprehensive

Poem

🐰✨ Path routing hops so clean and bright,
Wrapped components catch the light,
Hash or path, we navigate with grace,
Nuxt and Vue in perfect place,
Testing bounds to verify the flow! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: switching Nuxt's default routing strategy from hash-based to path-based across multiple UI components.

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

@wobsoriano
wobsoriano changed the base branch from main to vincent-and-the-doctorNovember 19, 2025 19:26
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components routing option to pathchore(nuxt): Default UI components to path routingNov 19, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Default UI components to path routingchore(nuxt): Change default routing strategy to path-based for UI componentsNov 20, 2025
@wobsorianowobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI componentschore(nuxt): Change default routing strategy to path-basedNov 20, 2025
@pkg-pr-new

pkg-pr-newBot commented Nov 20, 2025

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7260

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7260

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7260

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7260

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7260

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7260

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7260

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7260

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7260

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7260

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7260

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7260

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7260

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7260

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7260

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7260

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7260

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7260

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7260

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7260

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7260

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7260

commit: ca6612e

@wobsoriano

Copy link
Copy Markdown
MemberAuthor

@coderabbitai review

@coderabbitai

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

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 (9)
.changeset/thirty-cherries-pull.md (1)

1-12: Breaking change properly documented, consider adding migration guidance.

The major version bump is appropriate for this breaking routing strategy change. The changeset clearly lists affected components.

Consider whether a migration guide is needed. Users upgrading will need to:

  1. Update page routes from hash-based to path-based (e.g., /sign-in#//sign-in/)
  2. Adjust any custom routing configuration
  3. Update middleware that relies on hash routing

Would you like me to help create migration documentation or open an issue to track this?

integration/templates/nuxt-node/app/middleware/auth.global.js (1)

4-5: Middleware correctly targets /user-profile; consider whether /hash/sign-in should also be treated as public

The updated matchers and redirect to /user-profile look consistent with the new routing structure and tests.

If you expect /hash/sign-in to behave like the regular sign-in route for already-authenticated users (i.e., redirect them to /user-profile as well), you might want to include it in the public matcher:

- const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);+ const isPublicPage = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)', '/hash/sign-in(.*)']);

If the hash-based route is meant as an isolated example/demo, the current behavior is fine.

Also applies to: 9-15

packages/vue/src/errors/messages.ts (1)

41-45: Routing error messages are clear; consider framework-neutral examples

The new noPathProvidedError and incompatibleRoutingWithPathProvidedError strings line up with the useRoutingProps behavior and give actionable guidance.

Minor nit: the examples (path={'/my-path'}, routing='path') read like JSX. If you want the messages to feel more framework-neutral (and match Vue templates a bit better), you could switch to something like:

`Example: <${componentName} path="/my-path" />``To resolve this error, set routing="path" on <${componentName} /> ...`

Purely cosmetic; current wording is still understandable.

integration/tests/nuxt/navigation.test.ts (2)

7-20: Make teardown resilient to beforeAll failures

fakeUser is assumed to be initialized in afterAll; if createFakeUser or createBapiUser throws, afterAll will error when accessing fakeUser.deleteIfExists(), masking the original failure.

Consider making fakeUser optional and guarding in teardown, e.g. initializing it to null and checking before calling deleteIfExists.


66-75: Assert the URL when checking that user profile uses path routing

The test name implies verifying path routing, but it currently only checks for the Security header. To fully exercise the routing behavior, it would be stronger to also assert the URL, for example:

awaitu.page.goToRelative('/user-profile/security');awaitu.page.waitForURL(`${app.serverUrl}/user-profile/security`);awaitexpect(page.url()).not.toContain('#');awaitexpect(u.page.locator('.cl-headerTitle').filter({hasText: 'Security'})).toBeVisible();

This ensures the component is actually mounted via a path-based route with no hash fragment.

packages/vue/src/composables/useRoutingProps.ts (2)

13-18: Tighten propsValue initialization for clearer typing and semantics

const propsValue = toValue(props) || {}; can both (a) widen the inferred type of propsValue (to T | {}) and (b) treat falsy-but-valid values as “missing”. While callers are unlikely to pass such values, you can make this safer and more explicit by using nullish coalescing and preserving the generic type:

constpropsValue=(toValue(props)??{})asT;

This keeps propsValue strongly typed as T and only falls back when the source is null/undefined, matching the intended usage.


17-40: Clarify behavior when non‑path routing is combined with a path from routingOptions

For non‑'path' routing modes, the check:

if(propsValue.path){returnerrorThrower.throw(incompatibleRoutingWithPathProvidedError(componentName));}

only treats a path coming from props as incompatible; a path coming from routingOptionsValue is silently dropped by the final spread + path: undefined.

If the intent is that anypath provided alongside non‑path routing (hash, virtual, etc.) should be considered a configuration error, you might want to base the incompatibility check on the combined path (from either source) instead of propsValue.path alone. If the current behavior is intentional (wrappers providing a default path that can be overridden by routing: 'hash'), consider adding a short comment to make that contract explicit.

packages/nuxt/src/runtime/components/uiComponents.ts (2)

21-48: Clarify assumptions and edge cases in usePathnameWithoutSplatRouteParams

The helper is a nice way to normalize the mount path, but a few implicit assumptions are baked in:

  • It treats any array-valued route.params entry as a catch‑all and flattens all such arrays into a single splatRouteParam.
  • It then removes the first occurrence of that joined segment from route.path via .replace(splatRouteParam, ...).

If you ever end up with multiple array params or the same segment value repeated in the path, this could remove an earlier occurrence than intended. Also, the logic assumes Nuxt always represents catch‑all params as arrays, including the single‑segment case.

This is probably fine for the current /user-profile/[...rest] style routes, but it would be good to either (a) document these assumptions in a brief comment, or (b) tighten the behavior (e.g., only stripping a trailing splatRouteParam with an end‑anchored replace) to make it robust to future route shape changes.


81-119: Consider forwarding slots for non‑profile wrappers as well

UserProfile and OrganizationProfile forward slots to their base components:

return()=>h(BaseUserProfile,routingProps.value,slots);

while CreateOrganization, OrganizationList, SignIn, and SignUp only pass routingProps.value:

return()=>h(BaseSignIn,routingProps.value);

If any of these base components support slot‑based customization in @clerk/vue, existing Nuxt users relying on those slots could lose that behavior now that the wrapped versions are exported by default.

It may be worth aligning them with the profile wrappers by also forwarding slots (and adding any static properties that need to be preserved), or explicitly confirming that these components are not expected to use slots in Nuxt.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5b0c4db and ca6612e.

⛔ Files ignored due to path filters (2)
  • packages/vue/src/composables/__tests__/__snapshots__/useRoutingProps.test.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .changeset/thirty-cherries-pull.md (1 hunks)
  • .changeset/wild-bees-explode.md (1 hunks)
  • integration/templates/nuxt-node/app/middleware/auth.global.js (1 hunks)
  • integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in.vue (0 hunks)
  • integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1 hunks)
  • integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1 hunks)
  • integration/tests/nuxt/basic.test.ts (2 hunks)
  • integration/tests/nuxt/navigation.test.ts (1 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
  • packages/nuxt/src/runtime/components/index.ts (1 hunks)
  • packages/nuxt/src/runtime/components/uiComponents.ts (1 hunks)
  • packages/nuxt/tsup.config.ts (2 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1 hunks)
  • packages/vue/src/composables/useRoutingProps.ts (1 hunks)
  • packages/vue/src/errors/messages.ts (1 hunks)
  • packages/vue/src/internal.ts (1 hunks)
  • packages/vue/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • integration/templates/nuxt-node/app/pages/sign-in.vue
🔇 Additional comments (14)
packages/vue/package.json (1)

73-73: LGTM! Centralized version management.

The change to use catalog:repo aligns with the workspace catalog entry (Vue 3.5.21) and ensures consistent Vue versions across packages.

.changeset/wild-bees-explode.md (1)

1-5: LGTM! Changeset properly documents the minor version bump.

The changeset correctly specifies a minor version bump for the internal composable addition, which is appropriate for internal API changes.

packages/nuxt/tsup.config.ts (2)

29-29: LGTM! Correctly externalizing Vue.

Adding vue to the externals array ensures Vue is not bundled and will be resolved at runtime, which is the correct approach for this integration.


11-11: No issues found with the glob pattern.

The directory contains only 2 TypeScript files (index.ts and uiComponents.ts), both of which are legitimate component files. There are no test files (.test.ts, .spec.ts) or unintended utility files present, so the glob pattern ./src/runtime/components/*.ts appropriately captures only the intended component files.

packages/nuxt/package.json (1)

81-82: LGTM! Consistent dependency management.

Adding Vue as a dev dependency with catalog:repo aligns with the workspace catalog strategy and supports the new Vue-based component wrappers.

packages/vue/tsup.config.ts (1)

2-2: LGTM! Clean type import simplification.

Removing the unused Options type import simplifies the code while maintaining functionality, as defineConfig provides the necessary typing.

integration/templates/nuxt-node/app/pages/sign-in/[...slug].vue (1)

1-3: No changes needed — SignIn component is properly auto-imported.

Verification confirms that SignIn is explicitly registered for auto-import in packages/nuxt/src/module.ts (line 142-154). The module uses Nuxt's addComponent() function to register the component with the correct export name and file path, enabling it to be used without explicit imports in templates.

integration/templates/nuxt-node/app/pages/sign-up/[...slug].vue (1)

1-3: Sign-up catch-all page wiring looks correct

Template-only page using SignUp with signInUrl="/sign-in" is consistent with the new path-based routing defaults and the slug-based sign-up route structure.

packages/vue/src/internal.ts (1)

1-2: Internal export for useRoutingProps is appropriate

Exposing useRoutingProps from the internal entrypoint aligns with how Nuxt/runtime wrappers consume Vue internals and keeps the public API clean.

integration/templates/nuxt-node/app/pages/hash/sign-in/[...slug].vue (1)

1-3: Hash-based sign-in page matches the routing strategy

Using SignIn with routing="hash" under a dedicated /hash/sign-in/[...slug] route cleanly preserves hash-routing support alongside the new path-based default.

integration/tests/nuxt/basic.test.ts (1)

33-49: Test route updates align with new /user-profile redirect target

Switching the navigation target from /user to /user-profile in both the SSR data and redirect tests matches the updated middleware behavior and the new profile route. The flows still look coherent end-to-end.

Also applies to: 51-57

packages/vue/src/composables/__tests__/useRoutingProps.test.ts (1)

1-117: useRoutingProps test coverage looks solid

The suite exercises the key behaviors (default path routing, hash/virtual interactions, error paths, and precedence rules) and ties directly to the new error messages. Using JSON snapshots for the computed options keeps the assertions focused and maintainable.

packages/nuxt/src/module.ts (1)

140-188: The review comment's concern about APIKeys removal appears to be based on incorrect assumptions.

The code review assumes that APIKeys was previously auto-registered in the Nuxt module and has been removed in this PR. However, the evidence contradicts this:

  1. CHANGELOG says "Introduce": The Nuxt CHANGELOG entry states "Introduce <APIKeys /> component ([#7225])" — not "Remove" — suggesting this is a new addition to Nuxt, not a removal.

  2. No prior registration found: A complete scan of packages/nuxt/src/module.ts shows APIKeys has never appeared in the auto-registration lists, and no evidence exists of it being in a prior version.

  3. Intentional exclusion is reasonable: The APIKeys component is in "early access and not yet recommended for production use" (per the clerk-js codebase). Excluding experimental features from auto-registration is a valid design decision to avoid promoting unstable APIs to new users.

The concern about breaking existing apps that rely on <APIKeys> being auto-registered is unfounded if it was never auto-registered in Nuxt to begin with.

Likely an incorrect or invalid review comment.

packages/nuxt/src/runtime/components/index.ts (1)

1-1: Re-export change looks correct and preserves the public surface

Routing the six key components through ./uiComponents while leaving the rest sourced from @clerk/vue keeps the existing import API intact (import { SignIn } from '@clerk/nuxt') and cleanly swaps in the Nuxt-aware wrappers.

No issues spotted here.

Comment threadpnpm-workspace.yaml Outdated
typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the Vue version is current and secure.

Since my knowledge is from March 2025 and this is November 2025, please verify that Vue 3.5.21 is still an appropriate version choice.


🌐 Web query:

What is the latest stable version of Vue 3 as of November 2025?

💡 Result:

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025). [1]

Sources:
[1] GitHub — vuejs/core releases (v3.5.24).


Update Vue to the latest stable version (3.5.24).

The latest stable Vue 3 release is v3.5.24 (released November 7, 2025), but pnpm-workspace.yaml specifies 3.5.21. Update to 3.5.24 to receive recent bug fixes and security improvements.

🤖 Prompt for AI Agents
In pnpm-workspace.yaml around line 20, the Vue version is pinned to 3.5.21;
update that entry to 3.5.24. Edit the file to replace "vue: 3.5.21" with "vue:
3.5.24", then run your package manager (e.g., pnpm install or pnpm up vue
--latest) to update lockfiles and run the test/build suite to verify nothing
broke.

Comment threadpackages/nuxt/tsup.config.ts Outdated
@wobsoriano
wobsoriano merged commit f944623 into vincent-and-the-doctorNov 21, 2025
14 of 16 checks passed
@wobsoriano
wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for freeto 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.

3 participants

@wobsoriano@panteliselef@clerk-cookie