Skip to content

Conversation

@brkalow
Copy link
Member

@brkalow brkalow commented Jan 15, 2026

Summary

  • Introduces a new ui.shared.browser.js build variant that externalizes React dependencies, allowing the host application's React to be reused instead of bundling a separate copy
  • Adds @clerk/ui/register module to register React on globalThis.__clerkSharedModules for sharing with @clerk/ui
  • Adds clerkUiVariant option to explicitly control which variant to use
  • For @clerk/react users, the shared variant is automatically detected and enabled for compatible React versions
  • Extracts version checking logic into a testable utility with comprehensive unit tests
  • Adds development warnings for configuration fallbacks and React version mismatches

fixes USER-4440

Test plan

  • Unit tests added for version parsing and bounds checking (20 new tests)
  • All existing tests pass (328 total)
  • Build succeeds for @clerk/react package
  • Manual testing: verify bundle size reduction in a sample app using @clerk/react
  • Manual testing: verify fallback to non-shared variant when React version is incompatible

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Introduced a shared React variant to reduce bundle size by externalizing React.
    • Added @clerk/ui/register to let the host app expose React for Clerk UI reuse.
    • Added clerkUIVariant ('shared') configuration to select the shared UI build.
    • @clerk/react now auto-enables the shared variant when the app's React is compatible.

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

@changeset-bot
Copy link

changeset-bot bot commented Jan 15, 2026

🦋 Changeset detected

Latest commit: e35383c

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

This PR includes changesets to release 20 packages
Name Type
@clerk/ui Minor
@clerk/react Minor
@clerk/shared Patch
@clerk/astro Patch
@clerk/chrome-extension Patch
@clerk/vue Patch
@clerk/expo Patch
@clerk/nextjs Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/agent-toolkit Patch
@clerk/backend Patch
@clerk/clerk-js Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nuxt Patch
@clerk/testing Patch

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

@vercel
Copy link

vercel bot commented Jan 15, 2026

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

Project Deployment Review Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jan 23, 2026 8:22pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Adds a shared React variant that externalizes React from the UI bundle and lets a host app provide React via a global registry. Introduces a new export module @clerk/ui/register which populates globalThis.__clerkSharedModules, runtime version-check utilities and bounds parsing in @clerk/shared and @clerk/react, build updates to emit a ui.shared.browser.js bundle with externals, and a clerkUIVariant option ('shared' | '') with automatic selection when the host React is compatible.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 describes the main change: adding a shared React variant feature to reduce bundle size.
Linked Issues check ✅ Passed The PR implements all objectives from USER-4440: React version compatibility detection, host React instance reuse, registration via @clerk/ui/register, explicit configuration option, and fallback mechanisms.
Out of Scope Changes check ✅ Passed All changes relate directly to implementing the shared React variant feature. No unrelated modifications detected in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 15, 2026

Open in StackBlitz

@clerk/agent-toolkit

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

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e35383c

brkalow and others added 11 commits January 15, 2026 22:54
Introduces a "shared" variant of @clerk/ui that externalizes React
dependencies, allowing the host application's React to be reused
instead of bundling a separate copy.

Changes:
- Add @clerk/ui/register module to register React on globalThis
- Add ui.shared.browser.js build variant with externalized React
- Add React version compatibility checking in @clerk/react
- Add clerkUiVariant option to load the appropriate variant
- Make dev server React externalization conditional via --env shared

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract version checking logic into testable utility (versionCheck.ts)
- Add comprehensive unit tests for version parsing and bounds checking
- Move version compatibility check to module level for better performance
- Add dev warning when pnpm-workspace.yaml fallback is used
- Add warning for React version mismatch in register modules
- Remove redundant clerkUiVariant assignment in isomorphicClerk.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The new exports field in clerk-js/package.json blocked deep imports that
expo and chrome-extension packages depended on. This adds:
- ./internal/fapi export for FapiRequestInit/FapiResponse types (expo)
- ./no-rhc export for the no-RHC variant (chrome-extension)

Also updates expo to use the new clean import path.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename register/index.js to register/index.cjs to fix module syntax
  mismatch (package uses "type": "module" but file was CommonJS)
- Add register/index.d.cts for CJS type declarations to fix
  "Masquerading as ESM" attw error
- Update exports to point to correct file extensions
- Run pnpm dedupe to clean up lockfile

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The new exports field introduces ESM entry points (.mjs) with CJS type
declarations (.d.ts), which triggers attw's "Masquerading as CJS" warning.
This is expected behavior for this package's build setup, so we ignore
the false-cjs rule.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
type VersionBounds = [major: number, minMinor: number, maxMinor: number, minPatch: number];

// Read supported React range from pnpm-workspace.yaml catalogs and parse into bounds
function getClerkUiSupportedReactBounds(): VersionBounds[] {
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't love the logic here, doing some iteration...

…pendencies

- Remove hardcoded fallback version range in tsup.config.ts
- Start from package.json peerDependencies.react to determine version range
- If it's a catalog reference (catalog:XXX), resolve it from pnpm-workspace.yaml
- If it's already a version range, parse it directly
- Extract parseRangeToBounds to build-utils/ for testability
- Add comprehensive tests for version range parsing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ClerkScripts component was not passing clerkUiVariant to clerkUiScriptUrl(),
causing ui.browser.js to load instead of ui.shared.browser.js in Next.js apps.

- Export IS_REACT_SHARED_VARIANT_COMPATIBLE from @clerk/react/internal
- Use React version compatibility to determine default UI variant in Next.js
- Pass clerkUiVariant to opts, allowing user override via provider props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@brkalow
Copy link
Member Author

brkalow commented Jan 22, 2026

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @brkalow - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20260122040150
@clerk/astro 3.0.0-snapshot.v20260122040150
@clerk/backend 3.0.0-snapshot.v20260122040150
@clerk/chrome-extension 3.0.0-snapshot.v20260122040150
@clerk/clerk-js 6.0.0-snapshot.v20260122040150
@clerk/dev-cli 1.0.0-snapshot.v20260122040150
@clerk/expo 3.0.0-snapshot.v20260122040150
@clerk/expo-passkeys 1.0.0-snapshot.v20260122040150
@clerk/express 2.0.0-snapshot.v20260122040150
@clerk/fastify 2.6.9-snapshot.v20260122040150
@clerk/localizations 4.0.0-snapshot.v20260122040150
@clerk/msw 0.0.1-snapshot.v20260122040150
@clerk/nextjs 7.0.0-snapshot.v20260122040150
@clerk/nuxt 2.0.0-snapshot.v20260122040150
@clerk/react 6.0.0-snapshot.v20260122040150
@clerk/react-router 3.0.0-snapshot.v20260122040150
@clerk/shared 4.0.0-snapshot.v20260122040150
@clerk/tanstack-react-start 1.0.0-snapshot.v20260122040150
@clerk/testing 2.0.0-snapshot.v20260122040150
@clerk/ui 1.0.0-snapshot.v20260122040150
@clerk/upgrade 2.0.0-snapshot.v20260122040150
@clerk/vue 2.0.0-snapshot.v20260122040150

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20260122040150 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20260122040150 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20260122040150 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20260122040150 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20260122040150 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20260122040150 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20260122040150 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260122040150 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20260122040150 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20260122040150 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20260122040150 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.1-snapshot.v20260122040150 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20260122040150 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20260122040150 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20260122040150 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20260122040150 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20260122040150 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260122040150 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20260122040150 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20260122040150 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20260122040150 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20260122040150 --save-exact

@brkalow
Copy link
Member Author

brkalow commented Jan 22, 2026

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @brkalow - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20260122042543
@clerk/astro 3.0.0-snapshot.v20260122042543
@clerk/backend 3.0.0-snapshot.v20260122042543
@clerk/chrome-extension 3.0.0-snapshot.v20260122042543
@clerk/clerk-js 6.0.0-snapshot.v20260122042543
@clerk/dev-cli 1.0.0-snapshot.v20260122042543
@clerk/expo 3.0.0-snapshot.v20260122042543
@clerk/expo-passkeys 1.0.0-snapshot.v20260122042543
@clerk/express 2.0.0-snapshot.v20260122042543
@clerk/fastify 2.6.9-snapshot.v20260122042543
@clerk/localizations 4.0.0-snapshot.v20260122042543
@clerk/msw 0.0.1-snapshot.v20260122042543
@clerk/nextjs 7.0.0-snapshot.v20260122042543
@clerk/nuxt 2.0.0-snapshot.v20260122042543
@clerk/react 6.0.0-snapshot.v20260122042543
@clerk/react-router 3.0.0-snapshot.v20260122042543
@clerk/shared 4.0.0-snapshot.v20260122042543
@clerk/tanstack-react-start 1.0.0-snapshot.v20260122042543
@clerk/testing 2.0.0-snapshot.v20260122042543
@clerk/ui 1.0.0-snapshot.v20260122042543
@clerk/upgrade 2.0.0-snapshot.v20260122042543
@clerk/vue 2.0.0-snapshot.v20260122042543

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20260122042543 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20260122042543 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20260122042543 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20260122042543 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20260122042543 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20260122042543 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20260122042543 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260122042543 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20260122042543 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20260122042543 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20260122042543 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.1-snapshot.v20260122042543 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20260122042543 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20260122042543 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20260122042543 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20260122042543 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20260122042543 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260122042543 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20260122042543 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20260122042543 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20260122042543 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20260122042543 --save-exact

@brkalow
Copy link
Member Author

brkalow commented Jan 22, 2026

@Ephem I think that was because react-dom/client wasn't being included, in addition to what looks like some change in chunking behavior.

Even with that smaller entry chunk, the total size was about the same. Here is a comparison, loading the sign in component:

Section 1 (canary):
- 1.0 + 36.1 + 44.0 + 114 + 63.7 + 5.2 + 11.2 + 5.7 + 3.0 + 15.8 = 299.7 kB
 
Section 2 (19kb entry):
- 19.4 + 44.0 + 123 + 70.1 + 5.2 + 11.2 + 5.6 + 3.0 + 15.8 = 297.3 kB
  
Section 3 (latest snapshot):
- 33.9 + 114 + 63.8 + 11.2 + 5.6 + 3.0 + 15.8 + 5.2 = 252.5 kB (-15%)

@Ephem
Copy link
Member

Ephem commented Jan 22, 2026

Trying the latest snapshot seems to work! I am now getting the ui.shared.browser.js which I don't think I was getting yesterday.

I was trying the snapshot after the client-fix yesterday, so not sure if something changed since then or if I just missed something when testing. 😄

- Add `noExternal: ['@clerk/ui/register']` to tsup config to inline
  the registration code at build time instead of requiring it as a
  runtime dependency
- Rename `clerkUiVariant` to `clerkUIVariant` for consistency

This eliminates the runtime dependency on @clerk/ui for consumers
of @clerk/react while still enabling the shared React variant.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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

🤖 Fix all issues with AI agents
In `@packages/react/tsup.config.ts`:
- Around line 8-17: The file references the type VersionBounds in the
getClerkUiSupportedReactBounds() return signature but never imports it; add an
import for VersionBounds (where it's defined) at the top of
packages/react/tsup.config.ts so the TypeScript compiler can resolve the type
(update the existing import block to include VersionBounds alongside
name/version/peerDependencies or add a separate import from the module that
exports VersionBounds).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…rray

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move parseVersion, checkVersionAgainstBounds, isVersionCompatible, and
VersionBounds type from @clerk/react to @clerk/shared/versionCheck for
reuse across packages. The React package now imports from shared and
re-exports for backwards compatibility, keeping only the React-specific
computeReactVersionCompatibility and IS_REACT_SHARED_VARIANT_COMPATIBLE.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@brkalow brkalow merged commit d8bbc66 into main Jan 23, 2026
40 checks passed
@brkalow brkalow deleted the brkalow/sacramento branch January 23, 2026 21:05
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.

6 participants