feat(ui): Add unified ui prop with RSC support via conditional exports#7664
feat(ui): Add unified ui prop with RSC support via conditional exports#7664jacekradko wants to merge 43 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 41bc6f4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 20 packages
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 |
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
📝 WalkthroughWalkthroughThis PR introduces a new public UI API and refactors Clerk UI consumption across multiple packages. A new Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/vue/src/plugin.ts (1)
81-91: Misleading comment on fallback behavior.The comment states "fall back to clerkUiCtor (deprecated)" but the actual fallback is loading from CDN via
loadClerkUiScript, not checking aclerkUiCtorproperty. Consider updating the comment to accurately reflect the behavior:- // Check for ui.ctor first (new API), then fall back to clerkUiCtor (deprecated) + // Check for ui.ctor first (bundled UI), then fall back to loading from CDN
🧹 Nitpick comments (1)
packages/astro/src/internal/create-clerk-instance.ts (1)
118-122: Misleading comment: fallback is CDN loading, not clerkUiCtor.The comment mentions "fall back to clerkUiCtor (deprecated)" but the actual fallback is loading from CDN via
loadClerkUiScript. TheclerkUiCtoroption path was removed from this function.Suggested fix
- // Check for ui.ctor first (new API), then fall back to clerkUiCtor (deprecated) + // If ui.ctor is provided (bundled UI), use it directly; otherwise load from CDN const ctorFromUi = options?.ui?.ctor; if (ctorFromUi) { return ctorFromUi; }
6bbf272 to
5f6df9f
Compare
5f6df9f to
431aa50
Compare
Adds `ui` prop to ClerkProvider for specifying UI metadata.
Each SDK decides whether to use `ui.ctor` based on support level.
- `@clerk/ui` exports only `{ ui }` with version and ctor
- Chrome extension uses `ui.ctor` (verified to work)
- React, Vue, Astro use CDN loading (not verified for bundling yet)
- Omit `clerkUiCtor` from public ClerkProviderProps type
431aa50 to
c5ff0d1
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/astro/src/internal/create-clerk-instance.ts (1)
111-124:ui.ctoris not being used when provided.The PR objective states that when
ui.ctoris provided, it should be used instead of loading from CDN. However,getClerkUiEntryChunkalways loads the UI script from CDN and ignoresoptions.ui?.ctor. This contradicts the JSDoc inpackages/astro/src/types.ts(lines 39-43) which states: "When provided with a bundled UI viaui.ctor, it will be used instead of loading from CDN."Suggested fix to honor ui.ctor when provided
async function getClerkUiEntryChunk<TUi extends Ui = Ui>( options?: AstroClerkCreateInstanceParams<TUi>, ): Promise<ClerkUiConstructor> { + // Use bundled UI constructor if provided + if (options?.ui?.ctor) { + return options.ui.ctor; + } + await loadClerkUiScript(options); if (!window.__internal_ClerkUiCtor) { throw new Error('Failed to download latest Clerk UI. Contact support@clerk.com.'); } return window.__internal_ClerkUiCtor; }
🤖 Fix all issues with AI agents
In `@packages/vue/src/plugin.ts`:
- Around line 81-87: The code always calls loadClerkUiScript and ignores a
provided bundled constructor; update the clerkUiCtorPromise logic to first check
pluginOptions.ui?.ctor (or options.ui?.ctor) and resolve to that ctor if
present, otherwise call loadClerkUiScript and fall back to
window.__internal_ClerkUiCtor; ensure clerkUiCtorPromise returns the provided
ctor when available, and preserve the existing error throw if neither the
provided ctor nor the downloaded window.__internal_ClerkUiCtor is present.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/chrome-extension/src/react/ClerkProvider.tsx`:
- Around line 38-40: The spread uses an unsafe cast ({...(rest as any)}) to
sneak in clerkUiCtor, hiding a type mismatch; replace this by declaring an
internal extended props type (e.g., InternalClerkProviderProps) that extends
ClerkProviderProps and includes clerkUiCtor with the correct type, update the
component signature to use that internal type, remove the as any cast, and pass
clerkUiCtor={ui.ctor} and Clerk={clerkInstance} with proper typing so TypeScript
enforces compatibility (locate symbols: ClerkProvider.tsx, rest, clerkUiCtor,
ui.ctor, ClerkReactProvider, ClerkProviderProps, clerkInstance).
♻️ Duplicate comments (1)
packages/vue/src/plugin.ts (1)
81-87:ui.ctoris not being used when provided.Same issue as in the Astro integration: the code always loads the UI from CDN via
loadClerkUiScriptand ignorespluginOptions.ui?.ctor. This contradicts the documented behavior that bundled UI viaui.ctorshould be used instead of CDN loading.
🧹 Nitpick comments (1)
.changeset/shiny-owls-dance.md (1)
10-23: Consider adding migration guidance.The changeset describes the new
uiprop but doesn't mention the deprecation or removal ofclerkUiCtor. Since the PR objectives state "Omits clerkUiCtor from public ClerkProviderProps," consider adding explicit migration guidance to help users transition from the old API.📝 Suggested addition for migration guidance
Usage: ```tsx import { ui } from '@clerk/ui'; <ClerkProvider ui={ui}> ... </ClerkProvider>
+Migration note: The
clerkUiCtorprop has been removed from public types. Use theuiprop instead by importing theuiobject from@clerk/ui.</details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/react/src/isomorphicClerk.ts (1)
511-535: Add tests for the new bundledui.ctorpath and CDN fallback.Line 511 introduces a new early-return path for
ui.ctor. I don’t see tests in this PR; please add (or point to) coverage that validates the bundled constructor path and the CDN fallback to prevent regressions.
…dledUI escape hatch
|
Hey @jacekradko - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.2.9-snapshot.v20260203033021 --save-exact
npm i @clerk/astro@3.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/backend@3.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/chrome-extension@3.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/clerk-js@6.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/dev-cli@1.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/expo@3.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/express@2.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/fastify@2.7.0-snapshot.v20260203033021 --save-exact
npm i @clerk/localizations@4.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/msw@0.0.1-snapshot.v20260203033021 --save-exact
npm i @clerk/nextjs@7.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/nuxt@2.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/react@6.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/react-router@3.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/shared@4.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/testing@2.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/ui@1.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/upgrade@2.0.0-snapshot.v20260203033021 --save-exact
npm i @clerk/vue@2.0.0-snapshot.v20260203033021 --save-exact |
Simplify the ui prop to only accept the bundled ClerkUI constructor from @clerk/ui. Users can no longer pass version or url to override CDN loading through the ui prop. - Update Ui type to require ClerkUI and make version optional - Remove version/url params from loadClerkUIScript calls in React/Vue - Update Vue plugin tests to cover new behavior - CDN loading still works as default when ui prop is not provided
|
!snapshot |
|
Hey @jacekradko - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.2.9-snapshot.v20260204043403 --save-exact
npm i @clerk/astro@3.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/backend@3.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/chrome-extension@3.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/clerk-js@6.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/dev-cli@1.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/expo@3.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/express@2.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/fastify@2.7.0-snapshot.v20260204043403 --save-exact
npm i @clerk/localizations@4.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/msw@0.0.1-snapshot.v20260204043403 --save-exact
npm i @clerk/nextjs@7.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/nuxt@2.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/react@6.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/react-router@3.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/shared@4.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/testing@2.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/ui@1.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/upgrade@2.0.0-snapshot.v20260204043403 --save-exact
npm i @clerk/vue@2.0.0-snapshot.v20260204043403 --save-exact |
The Vue plugin was passing `ui: { ClerkUI: ... }` to clerk.load(), but
clerk-js expects `clerkUICtor` to initialize the UI. This caused Vue and
Nuxt integration tests to fail as components never rendered.
Also fixes Prettier formatting in @clerk/ui.
Enable users to import `ui` from `@clerk/ui` in Next.js App Router server components without requiring a separate client wrapper. - Add `server.ts` export without ClerkUI constructor (server-safe) - Add `react-server` condition to package.json exports - Add `__brand` runtime marker to identify valid UI objects - Update IsomorphicClerk to dynamically import ClerkUI when marker is detected - Make `ClerkUI` optional in `Ui` type to support marker exports
…ition - Add @clerk/ui dependency to Next.js App Router preset - Update layout.tsx to use ui prop from @clerk/ui - Add test verifying bundled UI build succeeds in server components
|
!snapshot |
|
Hey @jacekradko - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.3.0-snapshot.v20260205174408 --save-exact
npm i @clerk/astro@3.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/backend@3.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/chrome-extension@3.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/clerk-js@6.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/dev-cli@1.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/expo@3.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/express@2.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/fastify@2.7.0-snapshot.v20260205174408 --save-exact
npm i @clerk/localizations@4.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/msw@0.0.1-snapshot.v20260205174408 --save-exact
npm i @clerk/nextjs@7.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/nuxt@2.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/react@6.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/react-router@3.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/shared@4.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/testing@2.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/ui@1.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/upgrade@2.0.0-snapshot.v20260205174408 --save-exact
npm i @clerk/vue@2.0.0-snapshot.v20260205174408 --save-exact |
When the ui prop is passed to ClerkProvider, the bundled UI will be used instead of the CDN bundle. Skip rendering the preload link in this case to avoid unnecessary network requests.
|
!snapshot |
|
Hey @jacekradko - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.3.0-snapshot.v20260205220511 --save-exact
npm i @clerk/astro@3.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/backend@3.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/chrome-extension@3.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/clerk-js@6.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/dev-cli@1.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/expo@3.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/express@2.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/fastify@2.7.0-snapshot.v20260205220511 --save-exact
npm i @clerk/localizations@4.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/msw@0.0.1-snapshot.v20260205220511 --save-exact
npm i @clerk/nextjs@7.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/nuxt@2.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/react@6.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/react-router@3.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/shared@4.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/testing@2.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/ui@1.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/upgrade@2.0.0-snapshot.v20260205220511 --save-exact
npm i @clerk/vue@2.0.0-snapshot.v20260205220511 --save-exact |
- Replace clerkUICtor with the new ui prop from @clerk/ui - Remove unnecessary generic type parameter - Add migration note to changeset
Remove @clerk/ui dependency from the shared appRouter preset and the shared layout template to prevent Next.js 15 integration tests from hanging. The bundled UI build test now adds its own dependency and layout file inline.
nikosdouvlis
left a comment
There was a problem hiding this comment.
Really like the direction! approving to unblock, left a comment below
| '@clerk/ui': minor | ||
| '@clerk/react': minor | ||
| '@clerk/vue': minor | ||
| '@clerk/astro': minor |
There was a problem hiding this comment.
Does the Astro integration support the ui prop already or is this planned? I think getClerkUIEntryChunk for Astro doesn't check for ui.ClerkUI like React and Vue do
Summary
Refactors the UI loading architecture so users only interact with an opaque
uiprop from@clerk/ui. Each SDK internally decides whether to use the bundled UI constructor or load from CDN.Key addition: Added
react-serverconditional export so theuiprop works directly in Next.js App Router server components without requiring a client wrapper.Key Changes
ui={ui}- the object is opaque and brandedclerkUiCtorfrom publicIsomorphicClerkOptions(kept internally asui.ClerkUIfor SDK use)ui.ClerkUI)react-serverconditional export that provides a server-safe markerui.versionto pin the UI versionHow It Works
The
@clerk/uipackage uses conditional exports:server.js{ __brand, version }index.js{ __brand, version, ClerkUI }When IsomorphicClerk receives the
uiprop:ui.ClerkUIexists → use directly (bundled)ui.__brand === '__clerkUI'but noClerkUI→ dynamic import from@clerk/ui/entrySDK Behavior
ui={ui}directlyui.ClerkUIdirectlyui.ClerkUIdirectlyui.ClerkUIdirectlyUsage
Next.js App Router (server component):
Other SDKs:
Test plan
Summary by CodeRabbit
New Features
uiprop to ClerkProvider enabling bundled Clerk UI support across React, Vue, Astro, and Chrome Extensionuiexport from @clerk/ui package for direct UI bundling instead of CDN loadingTests