Skip to content

Conversation

@wobsoriano
Copy link
Member

@wobsoriano wobsoriano commented Nov 19, 2025

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
Copy link

vercel bot commented Nov 19, 2025

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

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Nov 21, 2025 4:23pm

@changeset-bot
Copy link

changeset-bot bot commented Nov 19, 2025

🦋 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
Name Type
@clerk/nuxt Major
@clerk/vue Minor

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
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

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 name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring 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 name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The 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-doctor November 19, 2025 19:26
@wobsoriano wobsoriano changed the title chore(nuxt): Default UI components routing option to path chore(nuxt): Default UI components to path routing Nov 19, 2025
@wobsoriano wobsoriano changed the title chore(nuxt): Default UI components to path routing chore(nuxt): Change default routing strategy to path-based for UI components Nov 20, 2025
@wobsoriano wobsoriano changed the title chore(nuxt): Change default routing strategy to path-based for UI components chore(nuxt): Change default routing strategy to path-based Nov 20, 2025
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 20, 2025

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
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 21, 2025

✅ 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (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:

await u.page.goToRelative('/user-profile/security');
await u.page.waitForURL(`${app.serverUrl}/user-profile/security`);
await expect(page.url()).not.toContain('#');
await expect(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:

const propsValue = (toValue(props) ?? {}) as T;

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) {
  return errorThrower.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 any path 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.

typescript: 5.8.3
zx: 8.8.5
rolldown: 1.0.0-beta.47
vue: 3.5.21
Copy link
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.

@wobsoriano wobsoriano merged commit f944623 into vincent-and-the-doctor Nov 21, 2025
14 of 16 checks passed
@wobsoriano wobsoriano deleted the rob/nuxt-routing-path branch November 21, 2025 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants