add signOutCallback to UserButton
Description
Clerk's signOut method offers a signOutCallback param which can be used for redirects, but can also be used for anything else a developer needs to do or clean up when signing a user out, such as cleaning up local storage, running analytics events, etc.
Previously, we had a prop on UserButton that allowed passing in a custom function in this way, but it was removed in Core 2 under the premise that it was only really used for redirecting after sign out and providing an after sign out URL prop would add simplicity and consistency with other component APIs.
I think this was a poorly considered change, as there are a variety of other things that developers could want to do on sign out and this is no longer possible unless you build your own UserButton component. We have received customer feedback confirming this need.
This PR re-introduces the signOutCallback parameter to UserButton, but without changing anything else. If both signOutCallback and afterSignOutUrl are passed, signOutCallback will override the afterSignOutUrl redirect behavior.
Checklist
- [ ]
pnpm testruns as expected. - [ ]
pnpm buildruns 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
- [x] 🌟 New feature
- [ ] 🔨 Breaking change
- [ ] 📖 Refactoring / dependency upgrade / documentation
- [ ] other:
Summary by CodeRabbit
- New Features
- UserButton now supports a
signOutCallbackprop to customize post-sign-out behavior, allowing developers to override default navigation with custom handlers for both single and multi-session scenarios.
- UserButton now supports a
⚠️ No Changeset found
Latest commit: ab20bf34f5818a3a6dda3ce06d86bfd85e23f1d9
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| clerk-js-sandbox | Preview | Comment | Oct 16, 2025 2:25pm |
Walkthrough
This PR adds support for a customizable signOutCallback prop to the UserButton component, enabling developers to override default post-sign-out navigation. Type definitions clarify callback return types by changing from void to undefined. The context layer conditionally invokes the callback or falls back to existing navigation behavior. Tests validate callback behavior in single and multi-session scenarios.
Changes
| Cohort / File(s) | Summary |
|---|---|
Type definitions packages/types/src/clerk.ts |
Added signOutCallback?: SignOutCallback to UserButtonProps. Updated return types of BeforeEmitCallback, SetActiveNavigate, SignOutCallback, and CustomNavigation from void to undefined for consistency. |
Component context packages/clerk-js/src/ui/contexts/components/UserButton.ts |
Integrated signOutCallback extraction from context. Navigation functions now conditionally use signOutCallback if provided, otherwise fall back to default navigation behavior (navigateAfterSignOut and navigateAfterMultiSessionSingleSignOut updated). |
Tests packages/clerk-js/src/ui/components/UserButton/__tests__/UserButton.test.tsx |
Added test suites for signOutCallback behavior covering single-session sign-out, multi-session sign-out, and per-session sign-out scenarios. Tests verify callback invocation, Clerk signOut calls, and that navigation is not performed when callback is provided. |
Sequence Diagram(s)
sequenceDiagram
participant User
participant UserButton
participant Context
participant Callback
participant Clerk
User->>UserButton: Trigger sign out
UserButton->>Context: navigateAfterSignOut called
alt signOutCallback provided
Context->>Callback: Invoke signOutCallback
Callback->>Clerk: Custom logic (e.g., redirect)
Note over Callback,Clerk: Navigation customized
else signOutCallback not provided
Context->>Clerk: redirectWithAuth (default navigation)
Note over Context,Clerk: Default navigation behavior
end
Estimated code review effort
🎯 2 (Simple) | ⏱️ ~12 minutes
The changes follow a straightforward pattern: adding optional callback support with backward compatibility. Logic is localized and well-tested. The main review effort involves verifying callback integration points and ensuring the fallback behavior remains intact.
Poem
🐰 A rabbit hops through sign-out flows,
With callbacks now to guide where one goes,
No nav by default—just custom control,
Types tightened up to achieve the goal,
~Thump thump! ✨
Pre-merge checks and finishing touches
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title Check | ✅ Passed | The title clearly and concisely summarizes the primary change by stating that the signOutCallback prop is being added to the UserButton component, which aligns precisely with the pull request’s main objective. |
| Docstring Coverage | ✅ Passed | No functions found in the changes. Docstring coverage check skipped. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
je.add-after-sign-out-callback-to-userbutton
Comment @coderabbitai help to get the list of available commands and usage tips.
@clerk/agent-toolkit
npm i https://pkg.pr.new/@clerk/agent-toolkit@7006
@clerk/astro
npm i https://pkg.pr.new/@clerk/astro@7006
@clerk/backend
npm i https://pkg.pr.new/@clerk/backend@7006
@clerk/chrome-extension
npm i https://pkg.pr.new/@clerk/chrome-extension@7006
@clerk/clerk-js
npm i https://pkg.pr.new/@clerk/clerk-js@7006
@clerk/dev-cli
npm i https://pkg.pr.new/@clerk/dev-cli@7006
@clerk/elements
npm i https://pkg.pr.new/@clerk/elements@7006
@clerk/clerk-expo
npm i https://pkg.pr.new/@clerk/clerk-expo@7006
@clerk/expo-passkeys
npm i https://pkg.pr.new/@clerk/expo-passkeys@7006
@clerk/express
npm i https://pkg.pr.new/@clerk/express@7006
@clerk/fastify
npm i https://pkg.pr.new/@clerk/fastify@7006
@clerk/localizations
npm i https://pkg.pr.new/@clerk/localizations@7006
@clerk/nextjs
npm i https://pkg.pr.new/@clerk/nextjs@7006
@clerk/nuxt
npm i https://pkg.pr.new/@clerk/nuxt@7006
@clerk/clerk-react
npm i https://pkg.pr.new/@clerk/clerk-react@7006
@clerk/react-router
npm i https://pkg.pr.new/@clerk/react-router@7006
@clerk/remix
npm i https://pkg.pr.new/@clerk/remix@7006
@clerk/shared
npm i https://pkg.pr.new/@clerk/shared@7006
@clerk/tanstack-react-start
npm i https://pkg.pr.new/@clerk/tanstack-react-start@7006
@clerk/testing
npm i https://pkg.pr.new/@clerk/testing@7006
@clerk/themes
npm i https://pkg.pr.new/@clerk/themes@7006
@clerk/types
npm i https://pkg.pr.new/@clerk/types@7006
@clerk/upgrade
npm i https://pkg.pr.new/@clerk/upgrade@7006
@clerk/vue
npm i https://pkg.pr.new/@clerk/vue@7006
commit: ab20bf3
@jescalan
UserButton's afterSignOutUrl has been marked for deprecation, indicating this should be handled globally. Could the new function live in ClerkProvider ?
/**
* Full URL or path to navigate to after sign out is complete
* @deprecated Configure `afterSignOutUrl` as a global configuration, either in `<ClerkProvider/>` or in `await Clerk.load()`.
*/
I'd strongly argue that the signOutCallback on signOut should only be used for navigation purposes. If a developer is using Clerk.signOut() they should navigate in callback, and then await for Clerk.signOut() to resolve and run any code they want afterwards.
Something to consider is that by adding a function as a prop, the developer would need to refactor to use our component/provider inside a client component, since functions as props are not allowed in server components.
I stumbled across this and have some questions.
@jescalan What's the usecases that has come up? I can imagine things like firing off analytics events, but curious if we've heard others?
UserButton's afterSignOutUrl has been marked for deprecation, indicating this should be handled globally. Could the new function live in ClerkProvider ?
Something to consider is that by adding a function as a prop, the developer would need to refactor to use our component/provider inside a client component, since functions as props are not allowed in server components.
@panteliselef Aren't these statements incompatible in the case of Next? The provider is and needs to be a server component, so we can't pass in a function there? I might be misunderstanding or missing something which is why I wanted to ask.
Analytics events and "cleanup of local data" from the user that submitted the request, which is reasonable. If they have stuff they put in local storage for example, that they'd want to flush out on sign out, this would make sense