Skip to content

Conversation

@jacekradko
Copy link
Member

@jacekradko jacekradko commented Jan 21, 2026

Summary

Fixes: USER-4231

This PR removes the clerkJSVariant: 'headless' option and replaces it with a new prefetchUI prop that controls whether the @clerk/ui script is prefetched.

Changes

  • Removed: clerkJSVariant: 'headless' option from all SDK packages
  • Added: prefetchUI prop (boolean | undefined) to control UI bundle prefetching
    • prefetchUI={false} - Skip prefetching the UI bundle (for custom UIs using Control Components)
    • prefetchUI omitted or undefined - Prefetch UI normally (default behavior)
  • Renamed: shouldLoadClerkUishouldPrefetchClerkUi helper function
  • Updated: Environment variable from CLERK_UI_DISABLED to CLERK_PREFETCH_UI_DISABLED

Usage

// Disable UI prefetching (e.g., when using Control Components for custom UI)
<ClerkProvider prefetchUI={false}>
  {children}
</ClerkProvider>

// Or via environment variable
NEXT_PUBLIC_CLERK_PREFETCH_UI_DISABLED=true

Packages Updated

  • @clerk/shared
  • @clerk/react
  • @clerk/nextjs
  • @clerk/vue
  • @clerk/astro
  • @clerk/react-router
  • @clerk/tanstack-react-start
  • @clerk/express

Checklist

  • pnpm build passes
  • Updated integration tests
  • Updated integration templates

Summary by CodeRabbit

  • New Features

    • Introduced prefetchUI option to control Clerk UI prefetching; added env toggle to disable UI prefetch.
    • Added a React Native “native” variant.
  • Chores

    • Removed legacy headless UI variant and related exports; simplified UI loading and public config shapes across integrations and SDKs.
    • Centralized prefetch decision logic and conditional UI script emission.
  • Tests

    • Added integration and unit tests for UI-prefetch/headless behavior.

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

nikosdouvlis and others added 8 commits January 9, 2026 13:15
why:
when using clerkJSVariant='headless', applications only need control components and don't require the full UI bundle. loading the unnecessary @clerk/ui script adds overhead without providing value.

what changed:
- clerk-script.tsx: conditionally render clerk-ui script tag only when clerkJSVariant !== 'headless'
- integration template: read NEXT_PUBLIC_CLERK_JS_VARIANT env var and pass to ClerkProvider

users can now set NEXT_PUBLIC_CLERK_JS_VARIANT='headless' to skip loading the ~100KB @clerk/ui bundle when using only control components.
why:
when using clerkJSVariant='headless', applications only need control components and don't require the full UI bundle. loading the unnecessary @clerk/ui script adds overhead without providing value.

what changed:
- build-clerk-hotload-script: skip generating clerk-ui script tag when clerkJsVariant === 'headless'
- create-clerk-instance: getClerkUiEntryChunk returns undefined for headless variant to skip client-side hot-loading

users can now set clerkJSVariant='headless' to skip loading the ~100KB @clerk/ui bundle when using only control components.
why:
when using clerkJSVariant='headless', applications only need control components and don't require the full UI bundle. loading the unnecessary @clerk/ui script adds overhead without providing value.

what changed:
isomorphicClerk's getClerkUiEntryChunk method now returns undefined when clerkJSVariant === 'headless', skipping the loadClerkUiScript call entirely.

users can now set clerkJSVariant='headless' to skip loading the ~100KB @clerk/ui bundle when using only control components.
why:
when using clerkJSVariant='headless', applications only need control components and don't require the full UI bundle. loading the unnecessary @clerk/ui script adds overhead without providing value.

what changed:
clerkPlugin now checks if clerkJSVariant === 'headless' and skips the loadClerkUiScript call, resolving the clerkUiCtorPromise to undefined instead.

users can now set clerkJSVariant='headless' to skip loading the ~100KB @clerk/ui bundle when using only control components.
why:
verify that the headless variant correctly skips clerk-ui script injection across the full integration stack (env var → prop → script rendering).

what changed:
created headless-variant.test.ts that sets CLERK_JS_VARIANT='headless' and asserts clerk-ui script is absent while clerk-js script is present.
…ition

The headless variant is no longer needed now that UI components have been
moved to @clerk/ui. The browser builds are now identical in size.

Changes:
- Add `react-native` export condition in package.json for Expo/RN
- Rename `clerkHeadless` build to `clerkNative` (no chunk splitting)
- Remove `clerkHeadlessBrowser` build (identical to regular browser)
- Update Expo to import from `@clerk/clerk-js` instead of `/headless`
- Deprecate `clerkJSVariant` option (now ignored)
- Delete headless source files and export directory

BREAKING CHANGE: `@clerk/clerk-js/headless` import path removed.
Expo/React Native users should import from `@clerk/clerk-js` directly.
@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: e67dfa5

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 21, 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 21, 2026 7:52pm

Request Review

….com:clerk/javascript into jrad/remove-headless-variant
clerkJSVariant is still used to control whether @clerk/ui loads.
The option just no longer affects the clerk-js URL since the
separate headless build has been removed.
Replace the clerkJSVariant: 'headless' pattern with a cleaner ui prop API:
- ui: false - Skip loading @clerk/ui (for custom UIs)
- ui: { version?, url? } - Load UI with specific version/URL
- ui: undefined (default) - Load UI normally

Also adds shouldLoadClerkUi() helper function to shared package.

Breaking change: clerkJSVariant is removed in favor of ui prop.
@jacekradko jacekradko changed the title [DO NOT MERGE] feat(clerk-js): remove headless variant feat(clerk-js): remove headless variant, add ui prop Jan 21, 2026
@jacekradko jacekradko marked this pull request as ready for review January 21, 2026 15:09
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

Replaces previous UI variant and per-UI URL configuration with a boolean prefetchUI flag and a new shouldPrefetchClerkUi helper. Removes headless bundle exports and entry points and introduces a clerk.native variant. Adds PUBLIC_CLERK_PREFETCH_UI_DISABLED / CLERK_PREFETCH_UI_DISABLED env flags and gates Clerk UI script prefetching and hotload emission on prefetchUI across integrations and SDKs (Astro, Next.js, Nuxt, React, Vue, Express, React Router, TanStack, Expo, Chrome extension). Updates types/env shapes and tests, including a new Playwright integration test.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% 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 accurately describes the main change: removing the headless variant from clerk-js, which is the central objective of this PR.
Linked Issues check ✅ Passed The PR fully addresses USER-4231 by removing headless bundle generation, replacing it with a prefetchUI boolean option that controls UI prefetching instead of variant selection.
Out of Scope Changes check ✅ Passed All changes are in-scope: removing headless variant support, replacing with prefetchUI boolean prop, updating environment variables, and adjusting related exports across all affected SDK packages.

✏️ 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.

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: 2

🤖 Fix all issues with AI agents
In @.changeset/light-eagles-stay.md:
- Around line 1-2: The changeset is empty and must document the breaking changes
introduced: explicitly list affected package names and semver bumps (use major
bumps for breaking changes), describe the removals of clerkJSVariant and the old
import path `@clerk/clerk-js/headless`, and note the new ui prop and its migration
steps; update the changeset body to include a short summary, per-package entries
(e.g., package-name: major), and a brief migration note explaining how to
replace/remove clerkJSVariant, how to update import paths to the new
entrypoints, and how to adopt the new ui prop so consumers can upgrade safely.

In `@packages/expo/src/provider/singleton/createClerkInstance.ts`:
- Around line 1-2: The import path for FapiRequestInit/FapiResponse is invalid;
instead derive request/response types from the public Clerk API: create a
Handler type from Parameters<Clerk["__internal_onBeforeRequest"]>[0], then
extract Req = Parameters<Handler>[0] and use Req as the type for the requestInit
parameter, and similarly extract the response type from
Clerk["__internal_onAfterResponse"] to replace FapiResponse usage; update
function signatures in createClerkInstance.ts to use these derived types
(referencing Clerk, __internal_onBeforeRequest, __internal_onAfterResponse,
Handler, and Req).
🧹 Nitpick comments (1)
packages/react-router/src/client/ReactRouterClerkProvider.tsx (1)

102-109: Type safety concern with as any casts.

The as any casts on lines 102 and 108 bypass TypeScript's type checking. While this may be a workaround for complex generic constraints with TUi, it could mask type mismatches at runtime.

Consider whether these casts can be replaced with more precise type assertions or if the underlying types can be aligned to avoid the need for any.

Comment on lines +1 to +2
---
---
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 | 🟠 Major

Empty changeset file - missing package entries and description.

This changeset is empty but the PR introduces breaking changes (removal of clerkJSVariant, @clerk/clerk-js/headless import path, new ui prop). The changeset should document affected packages and their semver bumps.

🤖 Prompt for AI Agents
In @.changeset/light-eagles-stay.md around lines 1 - 2, The changeset is empty
and must document the breaking changes introduced: explicitly list affected
package names and semver bumps (use major bumps for breaking changes), describe
the removals of clerkJSVariant and the old import path `@clerk/clerk-js/headless`,
and note the new ui prop and its migration steps; update the changeset body to
include a short summary, per-package entries (e.g., package-name: major), and a
brief migration note explaining how to replace/remove clerkJSVariant, how to
update import paths to the new entrypoints, and how to adopt the new ui prop so
consumers can upgrade safely.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 21, 2026

Open in StackBlitz

@clerk/agent-toolkit

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

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: b28c7be

@jacekradko jacekradko changed the title feat(clerk-js): remove headless variant, add ui prop feat(clerk-js): remove headless variant, add prefetchUI prop Jan 21, 2026
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/astro/src/integration/create-integration.ts`:
- Line 61: The environment variable name used in create-integration (the
buildEnvVarFromOption call passing 'PUBLIC_CLERK_PREFETCH_UI_DISABLED') doesn't
match the schema's name ('PUBLIC_CLERK_UI_DISABLED'); update the code so names
are consistent—either rename the buildEnvVarFromOption argument to
'PUBLIC_CLERK_UI_DISABLED' where used (e.g., the prefetchUI handling in
create-integration) or add 'PUBLIC_CLERK_PREFETCH_UI_DISABLED' to the env schema
(the object that defines import.meta.env entries around the env schema block
referenced near lines 169-171) so the variable has proper typing and Astro env
validation passes. Ensure the same change is applied consistently wherever
PUBLIC_CLERK_UI_DISABLED/PREFETCH_UI_DISABLED appears.

@jacekradko jacekradko changed the title feat(clerk-js): remove headless variant, add prefetchUI prop feat(clerk-js): remove headless variant Jan 21, 2026
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.

3 participants