Skip to content

clerk-js ignores new ClerkProvider options after the first load, so afterSignOutUrl keeps its first value #10538

Open

Description

@Joozty

Preliminary Checks

Reproduction

https://github.com/Joozty/clerk-nextjs-activity-repro

Publishable key

Not shared. The reproduction works with any development instance: put your own key in .env.local (see .env.example).

Description

clerk-js keeps the options from its first load(). If a prop of <ClerkProvider> changes later, clerk-js ignores the new value. For example, an app with two languages passes afterSignOutUrl="/en" or afterSignOutUrl="/sk". A user opens /en, clicks a link to /sk, and signs out. Clerk sends the user to /en, not to /sk.

This happens with or without cacheComponents. #5935 had the same cause for signInUrl. Its author closed it without a fix, and it is now locked.

Steps to reproduce:

  1. Clone the reproduction and run pnpm install.
  2. Copy .env.example to .env.local. Set the publishable key of your own development instance there.
  3. Run pnpm dev and open http://localhost:3000/en.
  4. Click the /sk link. The layout now renders <ClerkProvider afterSignOutUrl="/sk">.
  5. Click Read Clerk.buildAfterSignOutUrl().

Expected behavior:

http://localhost:3000/sk.

Actual behavior:

http://localhost:3000/en. <UserButton> signs out to the URL from clerk.buildAfterSignOutUrl() (see packages/ui/src/contexts/components/UserButton.ts). I tested this with a real account: after I click /sk and sign out, I land on /en.

Why it happens:

  • When you click /sk, the new ClerkProvider sends its options to clerk-js. Its effect calls __internal_updateProps({ options }), and the options include afterSignOutUrl: "/sk". I checked this in the browser.
  • Clerk.__internal_updateProps() in packages/clerk-js/src/core/clerk.ts merges the new options with this.#options. But it gives the result only to Clerk's UI. It never saves it in this.#options. Only load() sets this.#options.
  • buildAfterSignOutUrl(), buildSignInUrl(), buildSignUpUrl(), buildAfterSignInUrl() and buildAfterSignUpUrl() read this.#options. So they keep the first values.
  • There is a second, smaller problem in @clerk/react. The effect in packages/react/src/contexts/ClerkProvider.tsx that calls __internal_updateProps({ options }) depends only on [mergedOptions.localization]. So if one ClerkProvider stays mounted and only afterSignOutUrl changes, the new value is not even sent.

#4498 added the merge in __internal_updateProps. Its changeset says "Re-init window.Clerk options when ClerkProvider props change", but the code only gives the merged options to the UI.

Possible fix:

  1. In __internal_updateProps, save the merged options in this.#options. This alone should fix the reproduction.
  2. In @clerk/react, send the options again when any option changes, not only localization. This is for apps where one ClerkProvider stays mounted.

Workaround: custom code can call signOut({ redirectUrl }). But <UserButton> has no prop for the sign-out URL. By default, clerk-js loads from the Frontend API, not from node_modules, so a pnpm patch in the app can't fix it.

Related:

Environment

System:
    OS: macOS 27.2
    CPU: (10) arm64 Apple M4
    Memory: 73.23 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.15.0 - /Users/jozef/.volta/tools/image/node/24.15.0/bin/node
    npm: 11.12.1 - /Users/jozef/.volta/tools/image/node/24.15.0/bin/npm
    pnpm: 12.4.1 - /Users/jozef/.volta/bin/pnpm
  Browsers:
    Chrome: 156.0.8078.48
    Firefox: 159.0
    Safari: 27.2
  npmPackages:
    @clerk/nextjs: 7.9.22 => 7.9.22
    next: 16.4.3 => 16.4.3
    react: 19.3.0 => 19.3.0
    react-dom: 19.3.0 => 19.3.0
  Loaded from the Frontend API:
    @clerk/clerk-js: 6.41.0 (6.42.0 and main have the same code)
    @clerk/ui: 1.42.0

Activity

  1. added
    needs-triageA ticket that needs to be triaged by a team member
    on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageA ticket that needs to be triaged by a team member

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions