Skip to content

feat(expo-native-components): move native components into @clerk/expo-native-components - #9955

Draft
mikepitre wants to merge 8 commits into
mainfrom
mike/expo-native-package
Draft

mikepitre wants to merge 8 commits into
mainfrom
mike/expo-native-package

Conversation

@mikepitre

@mikepitre mikepitre commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Description

Moves everything in @clerk/expo that depends on the Clerk native SDKs (clerk-ios / clerk-android) into a new optional package, @clerk/expo-native-components. Apps that don't install it no longer get the Clerk native SDKs, the native module, native client sync, or the iOS 17 minimum.

What this gains

For apps that don't use native components:

  • No Clerk native SDKs in the app. Expo autolinks every native module in an installed package, so today every @clerk/expo build pulls in clerk-ios and clerk-android. After this change they're only linked when @clerk/expo-native-components is installed. A JS-only test app builds with no ClerkExpo/ClerkKit pods and no ClerkKit symbols in the iOS binary, and with no Clerk native classes in the Android APK.
  • No forced iOS 17 minimum. clerk-ios requires iOS 17, and @clerk/expo's plugin raised every app's deployment target to it. The JS-only test app builds at iOS 16.4.
  • No second Clerk client running in the background. ClerkProvider configures the native SDK in every native build today. That means extra /client and /environment requests at startup, token polling every 5s, refreshes when the app returns to the foreground, and the native↔JS sync engine, all in apps that never render a native component. None of it runs unless @clerk/expo-native-components is installed.
  • Fewer ways the build can break. The Swift Package dependency, Android packaging exclusions and Kotlin metadata flag only apply to apps that opt in. For example, React Native Screens' gamma mode corrupts Pods.xcodeproj for pods with Swift Package dependencies. JS-only apps no longer hit that.

For the SDK:

  • One opt-in rule: installing @clerk/expo-native-components turns native Clerk on. __experimental_disableNativeClientSync is no longer needed to keep JS-only apps unaffected.
  • Native SDK version bumps stay contained. clerk-ios and clerk-android releases only affect @clerk/expo-native-components users.

What it costs

This is a move plus wiring only. The JS sync engine is not rewritten: ClerkProvider, native client sync, useBiometricCredentials and useTrustedDevices stay in @clerk/expo and keep resolving the ClerkExpo native module by name, so they work when @clerk/expo-native-components is installed and are no-ops or unavailable when it isn't.

Moved to packages/expo-native-components (git mv):

  • All of ios/ and android/ (module, views, app delegate subscriber, bridge, Compose hosts, theme loading, biometric functions, native tests), ClerkExpo.podspec, android/build.gradle, expo-module.config.json, react-native.config.js, and codegenConfig.
  • src/native/* (now the package root: AuthView, UserProfileView, UserButton, useAuthViewState, custom pages API and types) and the native view specs, with their unit tests.
  • app.plugin.js native-SDK parts: iOS 17 deployment target, ClerkExpoVersion, Android META-INF exclusion and -Xskip-metadata-version-check, keychainService, theme. The theme validation tests moved with it.

@clerk/expo-native-components doesn't depend on @clerk/expo (it reads auth state through @clerk/react), so the two packages don't form a workspace cycle. Its config plugin still reports the installed @clerk/expo version in x-clerk-host-sdk-version: it writes it to ClerkExpoVersion in Info.plist and to clerkExpo.hostSdkVersion in gradle.properties, falling back to the @clerk/expo-native-components version when the plugin hasn't run. The plugin also fails prebuild with an upgrade message if the installed @clerk/expo still bundles the native module, since both would register the ClerkExpo pod and Android module.

Backwards compatibility in @clerk/expo (shipped as a minor since native components are beta):

  • @clerk/expo/native re-exports from @clerk/expo-native-components using a require() in try/catch, which Metro treats as an optional dependency. Without the package, rendering a component or calling a hook throws an error explaining how to install @clerk/expo-native-components and add its plugin. Its types come from a checked-in native/index.d.ts that re-exports @clerk/expo-native-components.
  • The @clerk/expo config plugin keeps the hosted auth intent filter, appleSignIn and faceIDPermission, and no longer forces iOS 17. When @clerk/expo-native-components is resolvable from the project, it applies that package's plugin (run once, forwarding keychainService / theme, with options on an explicit @clerk/expo-native-components entry taking precedence), so apps that only list @clerk/expo keep their native configuration. If it isn't installed and native-only options are passed, it warns with install instructions.
  • @clerk/expo-native-components is an optional peer dependency of @clerk/expo. Biometric credential errors now tell you to install @clerk/expo-native-components when the native module is missing.

The native fixture workflow now packs and installs @clerk/expo-native-components, and the fixture lists its plugin.

Recordings of both halves are attached in the comments: a JS-only app without @clerk/expo-native-components, and an app with it rendering AuthView, UserButton and UserProfileView.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs 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
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

🤖 Generated with Claude Code

mikepitre and others added 5 commits September 27, 2026 10:15
…kages/expo-native

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…elegate its config plugin

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… add changesets

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 170d56f

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

This PR includes changesets to release 2 packages
Name Type
@clerk/expo Minor
@clerk/expo-native-components Minor

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

vercel Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
clerk-js-sandbox Ready Ready Preview Sep 28, 2026 8:25pm UTC
swingset Ready Ready Preview Sep 28, 2026 8:25pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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

@clerk/expo-native now takes useAuth from @clerk/react, so the two workspace
packages no longer form a cycle. The config plugin records the installed
@clerk/expo version for the native host SDK header on both platforms via
Info.plist and gradle.properties, and fails early if the installed @clerk/expo
still bundles the native module.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikepitre

mikepitre commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Superseded by #9955 (comment) (re-recorded with full-frame-rate capture).

…geset

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikepitre

mikepitre commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Superseded by #9955 (comment) (re-recorded so push and modal animations are captured).

…/expo-native-components

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikepitre mikepitre changed the title feat(expo-native): move native components into @clerk/expo-native feat(expo-native-components): move native components into @clerk/expo-native-components Sep 28, 2026
@mikepitre

mikepitre commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Native components with @clerk/expo-native-components installed, recorded with full-frame-rate simulator capture (simctl io recordVideo) so transitions are visible. iPhone Air simulator (iOS 27), Expo SDK 57 app built from the current top of the stack with @clerk/expo + @clerk/expo-native-components and its config plugin.

  1. AuthView presented as a modal, then dismissed
  2. Sign-in with a test-mode email code
  3. The native UserButton in the header opens its account sheet, then it's dismissed
  4. UserProfileView pushed onto the navigation stack
components-v3-final.mp4

@mikepitre

Copy link
Copy Markdown
Contributor Author

JS-only app, re-recorded with full-frame-rate simulator capture (simctl io recordVideo). iPhone Air simulator (iOS 27), Expo SDK 57 app with this stack's @clerk/expo and without @clerk/expo-native-components (it also has @clerk/expo-biometrics from later in the stack, which doesn't depend on the Clerk native SDKs).

  • Podfile.lock has no ClerkExpo or ClerkKit pods, and the app's iOS deployment target stays at 16.4
  • The app binary has no ClerkKit / ClerkExpoModule symbols
  • On screen, requireOptionalNativeModule('ClerkExpo') reports "not linked" while JS email-code sign-in and sign-out work normally
jsonly-v2-final.mp4

This branch was successfully deployed

2 active deployments
Preview – swingset — 170d56f6 Deployed Sep 28, 2026 by vercel[bot]
Preview – clerk-js-sandbox — 170d56f6 Deployed Sep 28, 2026 by vercel[bot]
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.

1 participant