From e1346e20b9e9c695d33d2393c687dc7bf37f046b Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:15:49 -0400
Subject: [PATCH 1/8] refactor(expo-native): move native SDK code from
@clerk/expo into packages/expo-native
Co-Authored-By: Claude Opus 5.5 (1M context)
---
packages/{expo => expo-native}/.gitignore | 0
packages/{expo => expo-native}/android/build.gradle | 0
.../{expo => expo-native}/android/src/main/AndroidManifest.xml | 0
.../src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | 0
.../main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt | 0
.../android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt | 0
.../src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt | 0
.../java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt | 0
.../main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt | 0
.../test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt | 0
.../expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt | 0
packages/{expo => expo-native}/app.plugin.js | 0
packages/{expo => expo-native}/expo-module.config.json | 0
.../{expo => expo-native}/ios/ClerkAppDelegateSubscriber.swift | 0
packages/{expo => expo-native}/ios/ClerkAuthNativeView.swift | 0
packages/{expo => expo-native}/ios/ClerkExpo.podspec | 0
packages/{expo => expo-native}/ios/ClerkExpoModule.swift | 0
packages/{expo => expo-native}/ios/ClerkNativeBridge.swift | 0
packages/{expo => expo-native}/ios/ClerkNativeViewHost.swift | 0
.../{expo => expo-native}/ios/ClerkUserButtonNativeView.swift | 0
.../{expo => expo-native}/ios/ClerkUserProfileNativeView.swift | 0
.../ios/Tests/ClerkAuthNativeViewPaperTests.swift | 0
.../{expo => expo-native}/ios/Tests/ClerkNativeBridgeTests.swift | 0
.../ios/Tests/ClerkNativeViewHostTests.swift | 0
.../ios/Tests/ClerkUserProfileCustomPageStateTests.swift | 0
packages/{expo => expo-native}/react-native.config.js | 0
packages/{expo/src/native => expo-native/src}/AuthView.tsx | 0
packages/{expo/src/native => expo-native/src}/AuthView.types.ts | 0
.../src/native => expo-native/src}/EmbeddedNavigation.types.ts | 0
packages/{expo/src/native => expo-native/src}/UserButton.tsx | 0
.../src/native => expo-native/src}/UserProfileCustomPages.tsx | 0
packages/{expo/src/native => expo-native/src}/UserProfileView.tsx | 0
.../src/native => expo-native/src}/__tests__/AuthView.test.tsx | 0
.../src/native => expo-native/src}/__tests__/UserButton.test.tsx | 0
.../src}/__tests__/UserProfileCustomPages.test.tsx | 0
.../native => expo-native/src}/__tests__/UserProfileView.test.tsx | 0
.../{expo => expo-native}/src/__tests__/appPlugin.theme.test.js | 0
.../src}/__tests__/useAuthViewState.test.tsx | 0
packages/{expo/src/native => expo-native/src}/index.ts | 0
.../src/specs/NativeClerkAuthView.android.ts | 0
packages/{expo => expo-native}/src/specs/NativeClerkAuthView.ts | 0
.../src/specs/NativeClerkUserButtonView.android.ts | 0
.../{expo => expo-native}/src/specs/NativeClerkUserButtonView.ts | 0
.../src/specs/NativeClerkUserProfileView.android.ts | 0
.../{expo => expo-native}/src/specs/NativeClerkUserProfileView.ts | 0
.../src/specs/__tests__/native-view.web.test.ts | 0
packages/{expo/src/native => expo-native/src}/useAuthViewState.ts | 0
47 files changed, 0 insertions(+), 0 deletions(-)
rename packages/{expo => expo-native}/.gitignore (100%)
rename packages/{expo => expo-native}/android/build.gradle (100%)
rename packages/{expo => expo-native}/android/src/main/AndroidManifest.xml (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt (100%)
rename packages/{expo => expo-native}/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt (100%)
rename packages/{expo => expo-native}/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt (100%)
rename packages/{expo => expo-native}/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt (100%)
rename packages/{expo => expo-native}/app.plugin.js (100%)
rename packages/{expo => expo-native}/expo-module.config.json (100%)
rename packages/{expo => expo-native}/ios/ClerkAppDelegateSubscriber.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkAuthNativeView.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkExpo.podspec (100%)
rename packages/{expo => expo-native}/ios/ClerkExpoModule.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkNativeBridge.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkNativeViewHost.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkUserButtonNativeView.swift (100%)
rename packages/{expo => expo-native}/ios/ClerkUserProfileNativeView.swift (100%)
rename packages/{expo => expo-native}/ios/Tests/ClerkAuthNativeViewPaperTests.swift (100%)
rename packages/{expo => expo-native}/ios/Tests/ClerkNativeBridgeTests.swift (100%)
rename packages/{expo => expo-native}/ios/Tests/ClerkNativeViewHostTests.swift (100%)
rename packages/{expo => expo-native}/ios/Tests/ClerkUserProfileCustomPageStateTests.swift (100%)
rename packages/{expo => expo-native}/react-native.config.js (100%)
rename packages/{expo/src/native => expo-native/src}/AuthView.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/AuthView.types.ts (100%)
rename packages/{expo/src/native => expo-native/src}/EmbeddedNavigation.types.ts (100%)
rename packages/{expo/src/native => expo-native/src}/UserButton.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/UserProfileCustomPages.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/UserProfileView.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/__tests__/AuthView.test.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/__tests__/UserButton.test.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/__tests__/UserProfileCustomPages.test.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/__tests__/UserProfileView.test.tsx (100%)
rename packages/{expo => expo-native}/src/__tests__/appPlugin.theme.test.js (100%)
rename packages/{expo/src/native => expo-native/src}/__tests__/useAuthViewState.test.tsx (100%)
rename packages/{expo/src/native => expo-native/src}/index.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkAuthView.android.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkAuthView.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkUserButtonView.android.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkUserButtonView.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkUserProfileView.android.ts (100%)
rename packages/{expo => expo-native}/src/specs/NativeClerkUserProfileView.ts (100%)
rename packages/{expo => expo-native}/src/specs/__tests__/native-view.web.test.ts (100%)
rename packages/{expo/src/native => expo-native/src}/useAuthViewState.ts (100%)
diff --git a/packages/expo/.gitignore b/packages/expo-native/.gitignore
similarity index 100%
rename from packages/expo/.gitignore
rename to packages/expo-native/.gitignore
diff --git a/packages/expo/android/build.gradle b/packages/expo-native/android/build.gradle
similarity index 100%
rename from packages/expo/android/build.gradle
rename to packages/expo-native/android/build.gradle
diff --git a/packages/expo/android/src/main/AndroidManifest.xml b/packages/expo-native/android/src/main/AndroidManifest.xml
similarity index 100%
rename from packages/expo/android/src/main/AndroidManifest.xml
rename to packages/expo-native/android/src/main/AndroidManifest.xml
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt b/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
similarity index 100%
rename from packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
rename to packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
diff --git a/packages/expo/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt b/packages/expo-native/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
similarity index 100%
rename from packages/expo/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
rename to packages/expo-native/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
diff --git a/packages/expo/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt b/packages/expo-native/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
similarity index 100%
rename from packages/expo/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
rename to packages/expo-native/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
diff --git a/packages/expo/app.plugin.js b/packages/expo-native/app.plugin.js
similarity index 100%
rename from packages/expo/app.plugin.js
rename to packages/expo-native/app.plugin.js
diff --git a/packages/expo/expo-module.config.json b/packages/expo-native/expo-module.config.json
similarity index 100%
rename from packages/expo/expo-module.config.json
rename to packages/expo-native/expo-module.config.json
diff --git a/packages/expo/ios/ClerkAppDelegateSubscriber.swift b/packages/expo-native/ios/ClerkAppDelegateSubscriber.swift
similarity index 100%
rename from packages/expo/ios/ClerkAppDelegateSubscriber.swift
rename to packages/expo-native/ios/ClerkAppDelegateSubscriber.swift
diff --git a/packages/expo/ios/ClerkAuthNativeView.swift b/packages/expo-native/ios/ClerkAuthNativeView.swift
similarity index 100%
rename from packages/expo/ios/ClerkAuthNativeView.swift
rename to packages/expo-native/ios/ClerkAuthNativeView.swift
diff --git a/packages/expo/ios/ClerkExpo.podspec b/packages/expo-native/ios/ClerkExpo.podspec
similarity index 100%
rename from packages/expo/ios/ClerkExpo.podspec
rename to packages/expo-native/ios/ClerkExpo.podspec
diff --git a/packages/expo/ios/ClerkExpoModule.swift b/packages/expo-native/ios/ClerkExpoModule.swift
similarity index 100%
rename from packages/expo/ios/ClerkExpoModule.swift
rename to packages/expo-native/ios/ClerkExpoModule.swift
diff --git a/packages/expo/ios/ClerkNativeBridge.swift b/packages/expo-native/ios/ClerkNativeBridge.swift
similarity index 100%
rename from packages/expo/ios/ClerkNativeBridge.swift
rename to packages/expo-native/ios/ClerkNativeBridge.swift
diff --git a/packages/expo/ios/ClerkNativeViewHost.swift b/packages/expo-native/ios/ClerkNativeViewHost.swift
similarity index 100%
rename from packages/expo/ios/ClerkNativeViewHost.swift
rename to packages/expo-native/ios/ClerkNativeViewHost.swift
diff --git a/packages/expo/ios/ClerkUserButtonNativeView.swift b/packages/expo-native/ios/ClerkUserButtonNativeView.swift
similarity index 100%
rename from packages/expo/ios/ClerkUserButtonNativeView.swift
rename to packages/expo-native/ios/ClerkUserButtonNativeView.swift
diff --git a/packages/expo/ios/ClerkUserProfileNativeView.swift b/packages/expo-native/ios/ClerkUserProfileNativeView.swift
similarity index 100%
rename from packages/expo/ios/ClerkUserProfileNativeView.swift
rename to packages/expo-native/ios/ClerkUserProfileNativeView.swift
diff --git a/packages/expo/ios/Tests/ClerkAuthNativeViewPaperTests.swift b/packages/expo-native/ios/Tests/ClerkAuthNativeViewPaperTests.swift
similarity index 100%
rename from packages/expo/ios/Tests/ClerkAuthNativeViewPaperTests.swift
rename to packages/expo-native/ios/Tests/ClerkAuthNativeViewPaperTests.swift
diff --git a/packages/expo/ios/Tests/ClerkNativeBridgeTests.swift b/packages/expo-native/ios/Tests/ClerkNativeBridgeTests.swift
similarity index 100%
rename from packages/expo/ios/Tests/ClerkNativeBridgeTests.swift
rename to packages/expo-native/ios/Tests/ClerkNativeBridgeTests.swift
diff --git a/packages/expo/ios/Tests/ClerkNativeViewHostTests.swift b/packages/expo-native/ios/Tests/ClerkNativeViewHostTests.swift
similarity index 100%
rename from packages/expo/ios/Tests/ClerkNativeViewHostTests.swift
rename to packages/expo-native/ios/Tests/ClerkNativeViewHostTests.swift
diff --git a/packages/expo/ios/Tests/ClerkUserProfileCustomPageStateTests.swift b/packages/expo-native/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
similarity index 100%
rename from packages/expo/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
rename to packages/expo-native/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
diff --git a/packages/expo/react-native.config.js b/packages/expo-native/react-native.config.js
similarity index 100%
rename from packages/expo/react-native.config.js
rename to packages/expo-native/react-native.config.js
diff --git a/packages/expo/src/native/AuthView.tsx b/packages/expo-native/src/AuthView.tsx
similarity index 100%
rename from packages/expo/src/native/AuthView.tsx
rename to packages/expo-native/src/AuthView.tsx
diff --git a/packages/expo/src/native/AuthView.types.ts b/packages/expo-native/src/AuthView.types.ts
similarity index 100%
rename from packages/expo/src/native/AuthView.types.ts
rename to packages/expo-native/src/AuthView.types.ts
diff --git a/packages/expo/src/native/EmbeddedNavigation.types.ts b/packages/expo-native/src/EmbeddedNavigation.types.ts
similarity index 100%
rename from packages/expo/src/native/EmbeddedNavigation.types.ts
rename to packages/expo-native/src/EmbeddedNavigation.types.ts
diff --git a/packages/expo/src/native/UserButton.tsx b/packages/expo-native/src/UserButton.tsx
similarity index 100%
rename from packages/expo/src/native/UserButton.tsx
rename to packages/expo-native/src/UserButton.tsx
diff --git a/packages/expo/src/native/UserProfileCustomPages.tsx b/packages/expo-native/src/UserProfileCustomPages.tsx
similarity index 100%
rename from packages/expo/src/native/UserProfileCustomPages.tsx
rename to packages/expo-native/src/UserProfileCustomPages.tsx
diff --git a/packages/expo/src/native/UserProfileView.tsx b/packages/expo-native/src/UserProfileView.tsx
similarity index 100%
rename from packages/expo/src/native/UserProfileView.tsx
rename to packages/expo-native/src/UserProfileView.tsx
diff --git a/packages/expo/src/native/__tests__/AuthView.test.tsx b/packages/expo-native/src/__tests__/AuthView.test.tsx
similarity index 100%
rename from packages/expo/src/native/__tests__/AuthView.test.tsx
rename to packages/expo-native/src/__tests__/AuthView.test.tsx
diff --git a/packages/expo/src/native/__tests__/UserButton.test.tsx b/packages/expo-native/src/__tests__/UserButton.test.tsx
similarity index 100%
rename from packages/expo/src/native/__tests__/UserButton.test.tsx
rename to packages/expo-native/src/__tests__/UserButton.test.tsx
diff --git a/packages/expo/src/native/__tests__/UserProfileCustomPages.test.tsx b/packages/expo-native/src/__tests__/UserProfileCustomPages.test.tsx
similarity index 100%
rename from packages/expo/src/native/__tests__/UserProfileCustomPages.test.tsx
rename to packages/expo-native/src/__tests__/UserProfileCustomPages.test.tsx
diff --git a/packages/expo/src/native/__tests__/UserProfileView.test.tsx b/packages/expo-native/src/__tests__/UserProfileView.test.tsx
similarity index 100%
rename from packages/expo/src/native/__tests__/UserProfileView.test.tsx
rename to packages/expo-native/src/__tests__/UserProfileView.test.tsx
diff --git a/packages/expo/src/__tests__/appPlugin.theme.test.js b/packages/expo-native/src/__tests__/appPlugin.theme.test.js
similarity index 100%
rename from packages/expo/src/__tests__/appPlugin.theme.test.js
rename to packages/expo-native/src/__tests__/appPlugin.theme.test.js
diff --git a/packages/expo/src/native/__tests__/useAuthViewState.test.tsx b/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
similarity index 100%
rename from packages/expo/src/native/__tests__/useAuthViewState.test.tsx
rename to packages/expo-native/src/__tests__/useAuthViewState.test.tsx
diff --git a/packages/expo/src/native/index.ts b/packages/expo-native/src/index.ts
similarity index 100%
rename from packages/expo/src/native/index.ts
rename to packages/expo-native/src/index.ts
diff --git a/packages/expo/src/specs/NativeClerkAuthView.android.ts b/packages/expo-native/src/specs/NativeClerkAuthView.android.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkAuthView.android.ts
rename to packages/expo-native/src/specs/NativeClerkAuthView.android.ts
diff --git a/packages/expo/src/specs/NativeClerkAuthView.ts b/packages/expo-native/src/specs/NativeClerkAuthView.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkAuthView.ts
rename to packages/expo-native/src/specs/NativeClerkAuthView.ts
diff --git a/packages/expo/src/specs/NativeClerkUserButtonView.android.ts b/packages/expo-native/src/specs/NativeClerkUserButtonView.android.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkUserButtonView.android.ts
rename to packages/expo-native/src/specs/NativeClerkUserButtonView.android.ts
diff --git a/packages/expo/src/specs/NativeClerkUserButtonView.ts b/packages/expo-native/src/specs/NativeClerkUserButtonView.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkUserButtonView.ts
rename to packages/expo-native/src/specs/NativeClerkUserButtonView.ts
diff --git a/packages/expo/src/specs/NativeClerkUserProfileView.android.ts b/packages/expo-native/src/specs/NativeClerkUserProfileView.android.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkUserProfileView.android.ts
rename to packages/expo-native/src/specs/NativeClerkUserProfileView.android.ts
diff --git a/packages/expo/src/specs/NativeClerkUserProfileView.ts b/packages/expo-native/src/specs/NativeClerkUserProfileView.ts
similarity index 100%
rename from packages/expo/src/specs/NativeClerkUserProfileView.ts
rename to packages/expo-native/src/specs/NativeClerkUserProfileView.ts
diff --git a/packages/expo/src/specs/__tests__/native-view.web.test.ts b/packages/expo-native/src/specs/__tests__/native-view.web.test.ts
similarity index 100%
rename from packages/expo/src/specs/__tests__/native-view.web.test.ts
rename to packages/expo-native/src/specs/__tests__/native-view.web.test.ts
diff --git a/packages/expo/src/native/useAuthViewState.ts b/packages/expo-native/src/useAuthViewState.ts
similarity index 100%
rename from packages/expo/src/native/useAuthViewState.ts
rename to packages/expo-native/src/useAuthViewState.ts
From 5520da881558d3f2f5bd9049ff656c37204bd949 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:28:11 -0400
Subject: [PATCH 2/8] feat(expo-native): scaffold @clerk/expo-native package
Co-Authored-By: Claude Opus 5.5 (1M context)
---
packages/expo-native/LICENSE | 21 ++++
packages/expo-native/README.md | 107 ++++++++++++++++++
packages/expo-native/android/build.gradle | 20 +++-
packages/expo-native/app.plugin.d.ts | 5 +
packages/expo-native/app.plugin.js | 104 +++--------------
packages/expo-native/package.json | 83 ++++++++++++++
packages/expo-native/src/AuthView.tsx | 8 +-
packages/expo-native/src/UserButton.tsx | 6 +-
packages/expo-native/src/UserProfileView.tsx | 8 +-
.../src/__tests__/AuthView.test.tsx | 4 +-
.../src/__tests__/UserButton.test.tsx | 4 +-
.../src/__tests__/UserProfileView.test.tsx | 4 +-
.../src/__tests__/appPlugin.theme.test.js | 49 +-------
.../src/__tests__/useAuthViewState.test.tsx | 6 +-
packages/expo-native/src/index.ts | 6 +-
.../src/specs/NativeClerkModule.ts | 6 +
.../src/specs/NativeClerkModule.types.ts | 9 ++
.../src/specs/NativeClerkModule.web.ts | 3 +
packages/expo-native/src/useAuthViewState.ts | 6 +-
.../expo-native/src/utils/native-module.ts | 7 ++
.../expo-native/tsconfig.declarations.json | 16 +++
packages/expo-native/tsconfig.json | 26 +++++
packages/expo-native/tsconfig.test.json | 6 +
packages/expo-native/tsdown.config.mts | 33 ++++++
packages/expo-native/vitest.config.mts | 10 ++
packages/expo-native/vitest.setup.mts | 22 ++++
pnpm-lock.yaml | 25 ++++
27 files changed, 437 insertions(+), 167 deletions(-)
create mode 100644 packages/expo-native/LICENSE
create mode 100644 packages/expo-native/README.md
create mode 100644 packages/expo-native/app.plugin.d.ts
create mode 100644 packages/expo-native/package.json
create mode 100644 packages/expo-native/src/specs/NativeClerkModule.ts
create mode 100644 packages/expo-native/src/specs/NativeClerkModule.types.ts
create mode 100644 packages/expo-native/src/specs/NativeClerkModule.web.ts
create mode 100644 packages/expo-native/src/utils/native-module.ts
create mode 100644 packages/expo-native/tsconfig.declarations.json
create mode 100644 packages/expo-native/tsconfig.json
create mode 100644 packages/expo-native/tsconfig.test.json
create mode 100644 packages/expo-native/tsdown.config.mts
create mode 100644 packages/expo-native/vitest.config.mts
create mode 100644 packages/expo-native/vitest.setup.mts
diff --git a/packages/expo-native/LICENSE b/packages/expo-native/LICENSE
new file mode 100644
index 00000000000..66914b6af7c
--- /dev/null
+++ b/packages/expo-native/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Clerk, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/expo-native/README.md b/packages/expo-native/README.md
new file mode 100644
index 00000000000..35fe6587c9d
--- /dev/null
+++ b/packages/expo-native/README.md
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
@clerk/expo-native
+
+
+
+
+[](https://clerk.com/discord)
+[](https://clerk.com/docs?utm_source=github&utm_medium=clerk_expo_native)
+[](https://x.com/intent/follow?screen_name=clerk)
+
+[Changelog](https://github.com/clerk/javascript/blob/main/packages/expo-native/CHANGELOG.md)
+·
+[Report a Bug](https://github.com/clerk/javascript/issues/new?assignees=&labels=needs-triage&projects=&template=BUG_REPORT.yml)
+·
+[Request a Feature](https://feedback.clerk.com/roadmap)
+·
+[Get help](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native)
+
+
+
+## Getting Started
+
+`@clerk/expo-native` adds Clerk's prebuilt native components to an Expo app that uses [`@clerk/expo`](https://github.com/clerk/javascript/tree/main/packages/expo). It contains the native modules built on the Clerk iOS and Android SDKs:
+
+- **iOS**: [clerk-ios](https://github.com/clerk/clerk-ios) (SwiftUI)
+- **Android**: [clerk-android](https://github.com/clerk/clerk-android) (Jetpack Compose)
+
+Install it only if you use `AuthView`, `UserProfileView`, `UserButton`, or biometric credentials. Apps that only use `@clerk/expo` do not include the Clerk native SDKs.
+
+### Prerequisites
+
+- `@clerk/expo`
+- Expo 54 or later, with a [development build](https://docs.expo.dev/develop/development-builds/introduction/) (Expo Go is not supported)
+- iOS 17 or later
+
+### Installation
+
+```sh
+npx expo install @clerk/expo @clerk/expo-native
+```
+
+Add the config plugin alongside `@clerk/expo` in your app config, then rebuild your native app:
+
+```json
+{
+ "expo": {
+ "plugins": ["@clerk/expo", "@clerk/expo-native"]
+ }
+}
+```
+
+The plugin accepts the following options:
+
+- `keychainService`: keychain service identifier to share the session with app extensions.
+- `theme`: path to a JSON file with `colors`, `darkColors`, and `design` keys that styles the native components.
+
+## Usage
+
+```tsx
+import { useAuth } from '@clerk/expo';
+import { AuthView } from '@clerk/expo-native';
+
+export default function SignInScreen() {
+ const { isSignedIn } = useAuth();
+
+ if (isSignedIn) {
+ return null;
+ }
+
+ return ;
+}
+```
+
+For further information, guides, and examples visit the [Expo reference documentation](https://clerk.com/docs/references/expo/overview?utm_source=github&utm_medium=clerk_expo_native).
+
+## Support
+
+For help, visit our [support page](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native).
+
+## Community
+
+Join our [Discord community](https://clerk.com/discord) to connect with other developers.
+
+## Contributing
+
+We're open to all community contributions! If you'd like to contribute in any way, please read [our contribution guidelines](https://github.com/clerk/javascript/blob/main/docs/CONTRIBUTING.md) and [code of conduct](https://github.com/clerk/javascript/blob/main/docs/CODE_OF_CONDUCT.md).
+
+## Security
+
+`@clerk/expo-native` follows good practices of security, but 100% security cannot be assured.
+
+`@clerk/expo-native` is provided **"as is"** without any **warranty**. Use at your own risk.
+
+_For more information and to report security issues, please refer to our [security documentation](https://github.com/clerk/javascript/blob/main/docs/SECURITY.md)._
+
+## License
+
+This project is licensed under the **MIT license**.
+
+See [LICENSE](https://github.com/clerk/javascript/blob/main/packages/expo-native/LICENSE) for more information.
diff --git a/packages/expo-native/android/build.gradle b/packages/expo-native/android/build.gradle
index 8975e848f7d..5cb80b92dd8 100644
--- a/packages/expo-native/android/build.gradle
+++ b/packages/expo-native/android/build.gradle
@@ -13,8 +13,22 @@ applyKotlinExpoModulesCorePlugin()
group = 'com.clerk.expo'
version = '1.0.0'
-def clerkExpoPackageJson = new JsonSlurper().parse(new File(projectDir, "../package.json"))
-def clerkExpoVersion = clerkExpoPackageJson.version.toString()
+// The native SDKs report the @clerk/expo version in the x-clerk-host-sdk-version header.
+def resolveClerkExpoVersion() {
+ try {
+ def process = new ProcessBuilder("node", "--print", "require.resolve('@clerk/expo/package.json')")
+ .directory(projectDir)
+ .start()
+ def clerkExpoPackageJsonPath = process.inputStream.text.trim()
+ if (process.waitFor() == 0 && clerkExpoPackageJsonPath) {
+ return new JsonSlurper().parse(new File(clerkExpoPackageJsonPath)).version.toString()
+ }
+ } catch (Exception ignored) {
+ }
+ return new JsonSlurper().parse(new File(projectDir, "../package.json")).version.toString()
+}
+
+def clerkExpoVersion = resolveClerkExpoVersion()
// Dependency versions - centralized for easier updates
// See: https://docs.gradle.org/current/userguide/version_catalogs.html for app-level version catalogs
@@ -97,7 +111,7 @@ try {
}
} catch (Exception ignored) {
// Future Gradle versions with Isolated Projects may block cross-project configuration.
- // In that case, users should add '@clerk/expo' to their app.json plugins array instead,
+ // In that case, users should add '@clerk/expo-native' to their app.json plugins array instead,
// which applies the flag via the config plugin's withClerkAndroid.
}
diff --git a/packages/expo-native/app.plugin.d.ts b/packages/expo-native/app.plugin.d.ts
new file mode 100644
index 00000000000..76cfe1c1acd
--- /dev/null
+++ b/packages/expo-native/app.plugin.d.ts
@@ -0,0 +1,5 @@
+import type { ConfigPlugin } from '@expo/config-plugins';
+
+declare const withClerkExpoNative: ConfigPlugin<{ keychainService?: string; theme?: string } | void>;
+
+export = withClerkExpoNative;
diff --git a/packages/expo-native/app.plugin.js b/packages/expo-native/app.plugin.js
index f8483877ff3..f8faa236dea 100644
--- a/packages/expo-native/app.plugin.js
+++ b/packages/expo-native/app.plugin.js
@@ -1,6 +1,6 @@
/**
- * Expo config plugin for @clerk/clerk-expo
- * Automatically configures iOS and Android to work with Clerk native components
+ * Expo config plugin for @clerk/expo-native
+ * Configures iOS and Android for the Clerk native SDKs (clerk-ios / clerk-android)
*
* When this plugin is used:
* 1. iOS is configured with the required deployment target and metadata
@@ -9,13 +9,11 @@
* Native modules and views are registered via Expo Modules autolinking.
*/
const {
- AndroidConfig,
+ createRunOncePlugin,
withXcodeProject,
withDangerousMod,
withInfoPlist,
withAppBuildGradle,
- withAndroidManifest,
- withEntitlementsPlist,
} = require('@expo/config-plugins');
const path = require('path');
const fs = require('fs');
@@ -23,33 +21,14 @@ const packageJson = require('./package.json');
const CLERK_MIN_IOS_VERSION = '17.0';
-const addHostedAuthIntentFilter = (mainActivity, packageName) => {
- const callbackHost = `${packageName}.hosted-callback`;
- const intentFilters = mainActivity['intent-filter'] || [];
- const hasAndroidName = (entries, name) => entries?.some(entry => entry.$?.['android:name'] === name);
- const callbackIsRegistered = intentFilters.some(
- intentFilter =>
- hasAndroidName(intentFilter.action, 'android.intent.action.VIEW') &&
- hasAndroidName(intentFilter.category, 'android.intent.category.DEFAULT') &&
- hasAndroidName(intentFilter.category, 'android.intent.category.BROWSABLE') &&
- intentFilter.data?.some(
- data => data.$?.['android:scheme'] === 'clerk' && data.$?.['android:host'] === callbackHost,
- ),
- );
-
- if (callbackIsRegistered) {
- return;
+// The native SDKs report the @clerk/expo version in the x-clerk-host-sdk-version header.
+const resolveHostSdkVersion = projectRoot => {
+ try {
+ const paths = [projectRoot, process.cwd(), __dirname].filter(Boolean);
+ return require(require.resolve('@clerk/expo/package.json', { paths })).version;
+ } catch {
+ return packageJson.version;
}
-
- intentFilters.push({
- action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
- category: [
- { $: { 'android:name': 'android.intent.category.DEFAULT' } },
- { $: { 'android:name': 'android.intent.category.BROWSABLE' } },
- ],
- data: [{ $: { 'android:scheme': 'clerk', 'android:host': callbackHost } }],
- });
- mainActivity['intent-filter'] = intentFilters;
};
const withClerkIOS = config => {
@@ -110,8 +89,9 @@ const withClerkIOS = config => {
return config;
});
+ const hostSdkVersion = resolveHostSdkVersion(config._internal?.projectRoot);
config = withInfoPlist(config, modConfig => {
- modConfig.modResults.ClerkExpoVersion = packageJson.version;
+ modConfig.modResults.ClerkExpoVersion = hostSdkVersion;
return modConfig;
});
@@ -125,15 +105,6 @@ const withClerkIOS = config => {
const withClerkAndroid = config => {
console.log('✅ Clerk Android plugin loaded');
- config = withAndroidManifest(config, modConfig => {
- const packageName = config.android?.package;
- if (packageName) {
- const mainActivity = AndroidConfig.Manifest.getMainActivityOrThrow(modConfig.modResults);
- addHostedAuthIntentFilter(mainActivity, packageName);
- }
- return modConfig;
- });
-
return withAppBuildGradle(config, modConfig => {
let buildGradle = modConfig.modResults.contents;
@@ -186,15 +157,6 @@ const withClerkAndroid = config => {
});
};
-/**
- * Combined Clerk Expo plugin
- *
- * When this plugin is configured in app.json/app.config.js:
- * 1. iOS gets the deployment target and metadata required by Clerk native views
- * 2. Android gets packaging exclusions for dependency conflicts
- *
- * Native modules and views are registered via Expo Modules autolinking.
- */
/**
* Write ClerkKeychainService to Info.plist when keychainService is provided.
* This allows extension apps (watch, widget, app clip) to share the same
@@ -212,37 +174,6 @@ const withClerkKeychainService = (config, { keychainService } = {}) => {
});
};
-const withClerkFaceIDPermission = (config, { faceIDPermission } = {}) => {
- if (faceIDPermission === undefined) {
- return config;
- }
-
- if (typeof faceIDPermission !== 'string' || faceIDPermission.trim().length === 0) {
- throw new Error('Clerk: faceIDPermission must be a non-empty string');
- }
-
- return withInfoPlist(config, modConfig => {
- if (!Object.hasOwn(modConfig.modResults, 'NSFaceIDUsageDescription')) {
- modConfig.modResults.NSFaceIDUsageDescription = faceIDPermission;
- }
- return modConfig;
- });
-};
-
-/**
- * Add Sign in with Apple entitlement to the iOS app.
- * Required for the native Apple Sign In flow via ASAuthorizationController.
- */
-const withClerkAppleSignIn = config => {
- return withEntitlementsPlist(config, modConfig => {
- if (!modConfig.modResults['com.apple.developer.applesignin']) {
- modConfig.modResults['com.apple.developer.applesignin'] = ['Default'];
- console.log('✅ Added Sign in with Apple entitlement');
- }
- return modConfig;
- });
-};
-
/**
* Apply a custom theme to Clerk native components (iOS + Android).
*
@@ -363,23 +294,16 @@ const withClerkTheme = (config, props = {}) => {
return config;
};
-const withClerkExpo = (config, props = {}) => {
- const { appleSignIn = true } = props;
+const withClerkExpoNative = (config, props = {}) => {
config = withClerkIOS(config);
- if (appleSignIn !== false) {
- config = withClerkAppleSignIn(config);
- }
config = withClerkAndroid(config);
config = withClerkKeychainService(config, props);
- config = withClerkFaceIDPermission(config, props);
config = withClerkTheme(config, props);
return config;
};
-module.exports = withClerkExpo;
+module.exports = createRunOncePlugin(withClerkExpoNative, packageJson.name, packageJson.version);
module.exports._testing = {
- addHostedAuthIntentFilter,
- withClerkFaceIDPermission,
validateThemeJson,
isPlainObject,
VALID_COLOR_KEYS,
diff --git a/packages/expo-native/package.json b/packages/expo-native/package.json
new file mode 100644
index 00000000000..25e1addf9d4
--- /dev/null
+++ b/packages/expo-native/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "@clerk/expo-native",
+ "version": "0.0.1",
+ "description": "Native Clerk components and native SDK integration for Expo, powered by clerk-ios and clerk-android",
+ "keywords": [
+ "react",
+ "react-native",
+ "expo",
+ "clerk",
+ "auth",
+ "authentication",
+ "native"
+ ],
+ "homepage": "https://clerk.com/",
+ "bugs": {
+ "url": "https://github.com/clerk/javascript/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/clerk/javascript.git",
+ "directory": "packages/expo-native"
+ },
+ "license": "MIT",
+ "author": "Clerk",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./app.plugin.js": "./app.plugin.js",
+ "./package.json": "./package.json"
+ },
+ "main": "./dist/index.js",
+ "source": "./src/index.ts",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "android",
+ "ios",
+ "src/specs",
+ "expo-module.config.json",
+ "react-native.config.js",
+ "app.plugin.js",
+ "app.plugin.d.ts"
+ ],
+ "scripts": {
+ "build": "tsdown",
+ "build:declarations": "tsc -p tsconfig.declarations.json",
+ "clean": "rimraf ./dist",
+ "dev": "tsdown --watch",
+ "dev:pub": "pnpm dev -- --env.publish",
+ "format": "node ../../scripts/format-package.mjs",
+ "format:check": "node ../../scripts/format-package.mjs --check",
+ "lint": "eslint src",
+ "test": "vitest run",
+ "test:watch": "vitest watch"
+ },
+ "dependencies": {
+ "tslib": "catalog:repo"
+ },
+ "devDependencies": {
+ "@clerk/expo": "workspace:*",
+ "@expo/config-plugins": "^54.0.4",
+ "react-native": "^0.86.0"
+ },
+ "peerDependencies": {
+ "@clerk/expo": "workspace:^",
+ "expo": "catalog:peer-expo",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-native": ">=0.75"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "codegenConfig": {
+ "name": "ClerkExpoSpec",
+ "type": "all",
+ "jsSrcsDir": "src/specs"
+ }
+}
diff --git a/packages/expo-native/src/AuthView.tsx b/packages/expo-native/src/AuthView.tsx
index 3f4b51ee346..7734de4dd86 100644
--- a/packages/expo-native/src/AuthView.tsx
+++ b/packages/expo-native/src/AuthView.tsx
@@ -2,9 +2,9 @@ import { useCallback } from 'react';
import type { NativeSyntheticEvent } from 'react-native';
import { Text, View } from 'react-native';
-import NativeClerkAuthView from '../specs/NativeClerkAuthView';
-import { isNativeSupported } from '../utils/native-module';
import type { AuthViewProps } from './AuthView.types';
+import NativeClerkAuthView from './specs/NativeClerkAuthView';
+import { isNativeSupported } from './utils/native-module';
type AuthNativeEvent = NativeSyntheticEvent>;
@@ -24,7 +24,7 @@ type AuthNativeEvent = NativeSyntheticEvent>;
*
* @example
* ```tsx
- * import { AuthView } from '@clerk/expo/native';
+ * import { AuthView } from '@clerk/expo-native';
* import { useAuth } from '@clerk/expo';
*
* export default function SignInScreen() {
@@ -63,7 +63,7 @@ export function AuthView({
{!isNativeSupported
? 'Native AuthView is only available on iOS and Android'
- : 'Native AuthView requires the @clerk/expo plugin. Add "@clerk/expo" to your app.json plugins array.'}
+ : 'Native AuthView requires a development build with the @clerk/expo-native config plugin. Add "@clerk/expo-native" to your app.json plugins array and rebuild your native app.'}
);
diff --git a/packages/expo-native/src/UserButton.tsx b/packages/expo-native/src/UserButton.tsx
index a1406f0652e..f3038fd4efc 100644
--- a/packages/expo-native/src/UserButton.tsx
+++ b/packages/expo-native/src/UserButton.tsx
@@ -3,8 +3,7 @@ import { useMemo, useRef } from 'react';
import type { NativeSyntheticEvent } from 'react-native';
import { StyleSheet, useWindowDimensions } from 'react-native';
-import NativeClerkUserButtonView from '../specs/NativeClerkUserButtonView';
-import { isNativeSupported } from '../utils/native-module';
+import NativeClerkUserButtonView from './specs/NativeClerkUserButtonView';
import type {
NativeUserProfileNavigationHandle,
UserProfileCustomDestination,
@@ -16,6 +15,7 @@ import {
UserProfileCustomPageHosts,
useUserProfileCustomPages,
} from './UserProfileCustomPages';
+import { isNativeSupported } from './utils/native-module';
type CustomizableNativeUserButtonProps = ComponentProps> & {
customPages?: string;
@@ -47,7 +47,7 @@ export interface UserButtonProps {
*
* @example
* ```tsx
- * import { UserButton } from '@clerk/expo/native';
+ * import { UserButton } from '@clerk/expo-native';
*
* export default function Home() {
* return (
diff --git a/packages/expo-native/src/UserProfileView.tsx b/packages/expo-native/src/UserProfileView.tsx
index 3c39d955375..32aa4b5a6a9 100644
--- a/packages/expo-native/src/UserProfileView.tsx
+++ b/packages/expo-native/src/UserProfileView.tsx
@@ -3,9 +3,8 @@ import { useCallback, useMemo, useRef } from 'react';
import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
import { StyleSheet, Text, View } from 'react-native';
-import NativeClerkUserProfileView from '../specs/NativeClerkUserProfileView';
-import { isNativeSupported } from '../utils/native-module';
import type { EmbeddedNavigationProps } from './EmbeddedNavigation.types';
+import NativeClerkUserProfileView from './specs/NativeClerkUserProfileView';
import type {
NativeUserProfileNavigationHandle,
UserProfileCustomDestination,
@@ -17,6 +16,7 @@ import {
UserProfileCustomPageHosts,
useUserProfileCustomPages,
} from './UserProfileCustomPages';
+import { isNativeSupported } from './utils/native-module';
type CustomizableNativeUserProfileProps = ComponentProps> & {
customPages?: string;
@@ -75,7 +75,7 @@ export interface UserProfileViewProps extends EmbeddedNavigationProps {
*
* @example
* ```tsx
- * import { UserProfileView } from '@clerk/expo/native';
+ * import { UserProfileView } from '@clerk/expo-native';
* import { useAuth } from '@clerk/expo';
*
* export default function ProfileScreen() {
@@ -128,7 +128,7 @@ export function UserProfileView({
{!isNativeSupported
? 'Native UserProfileView is only available on iOS and Android'
- : 'Native UserProfileView requires the @clerk/expo plugin. Add "@clerk/expo" to your app.json plugins array.'}
+ : 'Native UserProfileView requires a development build with the @clerk/expo-native config plugin. Add "@clerk/expo-native" to your app.json plugins array and rebuild your native app.'}
);
diff --git a/packages/expo-native/src/__tests__/AuthView.test.tsx b/packages/expo-native/src/__tests__/AuthView.test.tsx
index 6b3029ecdda..528e251fb2e 100644
--- a/packages/expo-native/src/__tests__/AuthView.test.tsx
+++ b/packages/expo-native/src/__tests__/AuthView.test.tsx
@@ -10,13 +10,13 @@ const mocks = vi.hoisted(() => {
};
});
-vi.mock('../../specs/NativeClerkAuthView', () => {
+vi.mock('../specs/NativeClerkAuthView', () => {
return {
default: mocks.NativeClerkAuthView,
};
});
-vi.mock('../../utils/native-module', () => {
+vi.mock('../utils/native-module', () => {
return {
isNativeSupported: true,
};
diff --git a/packages/expo-native/src/__tests__/UserButton.test.tsx b/packages/expo-native/src/__tests__/UserButton.test.tsx
index d4eaef80382..d414aa0f89f 100644
--- a/packages/expo-native/src/__tests__/UserButton.test.tsx
+++ b/packages/expo-native/src/__tests__/UserButton.test.tsx
@@ -8,14 +8,14 @@ const mocks = vi.hoisted(() => ({
nativeProps: vi.fn(),
}));
-vi.mock('../../specs/NativeClerkUserButtonView', () => ({
+vi.mock('../specs/NativeClerkUserButtonView', () => ({
default: React.forwardRef((props: Record, _ref) => {
mocks.nativeProps(props);
return null;
}),
}));
-vi.mock('../../utils/native-module', () => ({
+vi.mock('../utils/native-module', () => ({
isNativeSupported: true,
}));
diff --git a/packages/expo-native/src/__tests__/UserProfileView.test.tsx b/packages/expo-native/src/__tests__/UserProfileView.test.tsx
index 34af7275e6d..619c417305e 100644
--- a/packages/expo-native/src/__tests__/UserProfileView.test.tsx
+++ b/packages/expo-native/src/__tests__/UserProfileView.test.tsx
@@ -14,7 +14,7 @@ const mocks = vi.hoisted(() => {
};
});
-vi.mock('../../specs/NativeClerkUserProfileView', () => {
+vi.mock('../specs/NativeClerkUserProfileView', () => {
return {
default: React.forwardRef((props: { children?: React.ReactNode }, ref) => {
React.useImperativeHandle(ref, () => ({ navigateCustomPage: mocks.navigateCustomPage }));
@@ -24,7 +24,7 @@ vi.mock('../../specs/NativeClerkUserProfileView', () => {
};
});
-vi.mock('../../utils/native-module', () => {
+vi.mock('../utils/native-module', () => {
return {
isNativeSupported: true,
};
diff --git a/packages/expo-native/src/__tests__/appPlugin.theme.test.js b/packages/expo-native/src/__tests__/appPlugin.theme.test.js
index fcff7577b0e..c0f89d02ab8 100644
--- a/packages/expo-native/src/__tests__/appPlugin.theme.test.js
+++ b/packages/expo-native/src/__tests__/appPlugin.theme.test.js
@@ -2,54 +2,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
// eslint-disable-next-line @typescript-eslint/no-require-imports -- CJS plugin, no ESM export
const clerkPlugin = require('../../app.plugin.js');
-const { withClerkFaceIDPermission, validateThemeJson } = clerkPlugin._testing;
-
-function applyInfoPlistMod(config, modResults) {
- return config.mods.ios.infoPlist({
- ...config,
- modRequest: {},
- modResults,
- });
-}
-
-describe('withClerkFaceIDPermission', () => {
- test('adds the configured Face ID usage description', async () => {
- const config = withClerkFaceIDPermission(
- { name: 'test', slug: 'test' },
- { faceIDPermission: 'Allow $(PRODUCT_NAME) to use Face ID for secure sign-in.' },
- );
-
- const result = await applyInfoPlistMod(config, {});
-
- expect(result.modResults.NSFaceIDUsageDescription).toBe('Allow $(PRODUCT_NAME) to use Face ID for secure sign-in.');
- });
-
- test('preserves an app-provided Face ID usage description', async () => {
- const config = withClerkFaceIDPermission(
- { name: 'test', slug: 'test' },
- { faceIDPermission: 'Clerk-provided description' },
- );
-
- const result = await applyInfoPlistMod(config, {
- NSFaceIDUsageDescription: 'App-provided description',
- });
-
- expect(result.modResults.NSFaceIDUsageDescription).toBe('App-provided description');
- });
-
- test('does not configure the Info.plist without an explicit permission description', () => {
- const config = { name: 'test', slug: 'test' };
-
- expect(withClerkFaceIDPermission(config)).toBe(config);
- expect(config).not.toHaveProperty('mods');
- });
-
- test.each([null, '', ' ', true])('rejects an invalid permission description: %j', faceIDPermission => {
- expect(() => withClerkFaceIDPermission({ name: 'test', slug: 'test' }, { faceIDPermission })).toThrow(
- 'faceIDPermission must be a non-empty string',
- );
- });
-});
+const { validateThemeJson } = clerkPlugin._testing;
describe('validateThemeJson', () => {
beforeEach(() => {
diff --git a/packages/expo-native/src/__tests__/useAuthViewState.test.tsx b/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
index 988f2a57729..aab5496325c 100644
--- a/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
+++ b/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
@@ -1,7 +1,7 @@
import { act, cleanup, renderHook, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
-import type { NativeAuthFlowState } from '../../specs/NativeClerkModule.types';
+import type { NativeAuthFlowState } from '../specs/NativeClerkModule.types';
import { useAuthViewState } from '../useAuthViewState';
const mocks = vi.hoisted(() => ({
@@ -13,11 +13,11 @@ const mocks = vi.hoisted(() => ({
remove: vi.fn(),
}));
-vi.mock('../../hooks/useAuth', () => ({
+vi.mock('@clerk/expo', () => ({
useAuth: () => mocks.auth,
}));
-vi.mock('../../utils/native-module', () => ({
+vi.mock('../utils/native-module', () => ({
get ClerkExpoModule() {
return mocks.module;
},
diff --git a/packages/expo-native/src/index.ts b/packages/expo-native/src/index.ts
index 2a62047dd85..a20538f6186 100644
--- a/packages/expo-native/src/index.ts
+++ b/packages/expo-native/src/index.ts
@@ -7,12 +7,12 @@
*
* ## Installation
*
- * Native components require the `@clerk/expo` plugin to be configured in your `app.json`:
+ * Native components require the `@clerk/expo-native` config plugin in your `app.json`, alongside `@clerk/expo`:
*
* ```json
* {
* "expo": {
- * "plugins": ["@clerk/expo"]
+ * "plugins": ["@clerk/expo", "@clerk/expo-native"]
* }
* }
* ```
@@ -25,7 +25,7 @@
* - {@link UserProfileView} - User profile and account management, renders inline
* - {@link UserButton} - Avatar button that opens the native user profile
*
- * @module @clerk/expo/native
+ * @module @clerk/expo-native
*/
export { AuthView } from './AuthView';
diff --git a/packages/expo-native/src/specs/NativeClerkModule.ts b/packages/expo-native/src/specs/NativeClerkModule.ts
new file mode 100644
index 00000000000..072d011f539
--- /dev/null
+++ b/packages/expo-native/src/specs/NativeClerkModule.ts
@@ -0,0 +1,6 @@
+import { requireOptionalNativeModule } from 'expo';
+
+import type { Spec } from './NativeClerkModule.types';
+
+// Optional so it resolves to null in Expo Go instead of throwing at import time.
+export default requireOptionalNativeModule('ClerkExpo');
diff --git a/packages/expo-native/src/specs/NativeClerkModule.types.ts b/packages/expo-native/src/specs/NativeClerkModule.types.ts
new file mode 100644
index 00000000000..9df94448a91
--- /dev/null
+++ b/packages/expo-native/src/specs/NativeClerkModule.types.ts
@@ -0,0 +1,9 @@
+export type NativeAuthFlowState = {
+ isLoaded: boolean;
+ isAuthFlowComplete: boolean;
+};
+
+export interface Spec {
+ addListener?(eventName: string, listener?: (...args: unknown[]) => void): { remove: () => void };
+ getAuthFlowState?(): Promise;
+}
diff --git a/packages/expo-native/src/specs/NativeClerkModule.web.ts b/packages/expo-native/src/specs/NativeClerkModule.web.ts
new file mode 100644
index 00000000000..625a2ed22ae
--- /dev/null
+++ b/packages/expo-native/src/specs/NativeClerkModule.web.ts
@@ -0,0 +1,3 @@
+import type { Spec } from './NativeClerkModule.types';
+
+export default null as Spec | null;
diff --git a/packages/expo-native/src/useAuthViewState.ts b/packages/expo-native/src/useAuthViewState.ts
index ecb793f84f1..a4c4a5f3b61 100644
--- a/packages/expo-native/src/useAuthViewState.ts
+++ b/packages/expo-native/src/useAuthViewState.ts
@@ -1,8 +1,8 @@
+import { useAuth } from '@clerk/expo';
import { useEffect, useState } from 'react';
-import { useAuth } from '../hooks/useAuth';
-import type { NativeAuthFlowState } from '../specs/NativeClerkModule.types';
-import { ClerkExpoModule as ClerkExpo } from '../utils/native-module';
+import type { NativeAuthFlowState } from './specs/NativeClerkModule.types';
+import { ClerkExpoModule as ClerkExpo } from './utils/native-module';
const nativeAuthFlowChangedEvent = 'clerkNativeAuthFlowChanged';
diff --git a/packages/expo-native/src/utils/native-module.ts b/packages/expo-native/src/utils/native-module.ts
new file mode 100644
index 00000000000..a416ccd8917
--- /dev/null
+++ b/packages/expo-native/src/utils/native-module.ts
@@ -0,0 +1,7 @@
+import { Platform } from 'react-native';
+
+import NativeClerkModule from '../specs/NativeClerkModule';
+
+export const isNativeSupported = Platform.OS === 'ios' || Platform.OS === 'android';
+
+export const ClerkExpoModule = isNativeSupported ? NativeClerkModule : null;
diff --git a/packages/expo-native/tsconfig.declarations.json b/packages/expo-native/tsconfig.declarations.json
new file mode 100644
index 00000000000..ac04a85ce27
--- /dev/null
+++ b/packages/expo-native/tsconfig.declarations.json
@@ -0,0 +1,16 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "./src",
+ "incremental": false,
+ "skipLibCheck": true,
+ "noEmit": false,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "declarationMap": true,
+ "sourceMap": false,
+ "declarationDir": "./dist"
+ },
+ "include": ["src"],
+ "exclude": ["**/__tests__/**/*", "app.plugin.js"]
+}
diff --git a/packages/expo-native/tsconfig.json b/packages/expo-native/tsconfig.json
new file mode 100644
index 00000000000..5510c6beb95
--- /dev/null
+++ b/packages/expo-native/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "outDir": "dist",
+ "lib": ["es2019", "dom"],
+ "jsx": "react-jsx",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "importHelpers": true,
+ "declaration": true,
+ "declarationMap": false,
+ "noImplicitReturns": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "resolveJsonModule": true,
+ "sourceMap": false,
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "allowJs": true,
+ "target": "ES2019",
+ "noEmitOnError": false,
+ "incremental": true,
+ "moduleSuffixes": [".web", ".ios", ".android", ".native", ""]
+ },
+ "include": ["src", "app.plugin.js"]
+}
diff --git a/packages/expo-native/tsconfig.test.json b/packages/expo-native/tsconfig.test.json
new file mode 100644
index 00000000000..5635d6cd1b7
--- /dev/null
+++ b/packages/expo-native/tsconfig.test.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "sourceMap": true
+ }
+}
diff --git a/packages/expo-native/tsdown.config.mts b/packages/expo-native/tsdown.config.mts
new file mode 100644
index 00000000000..e84f7249174
--- /dev/null
+++ b/packages/expo-native/tsdown.config.mts
@@ -0,0 +1,33 @@
+import type { Options } from 'tsdown';
+import { defineConfig } from 'tsdown';
+
+import { runAfterLast } from '../../scripts/utils.ts';
+
+function preserveRelativeImports(id: string) {
+ return id.startsWith('.');
+}
+
+export default defineConfig(overrideOptions => {
+ const isWatch = !!overrideOptions.watch;
+ const shouldPublish = !!overrideOptions.env?.publish;
+
+ const options: Options = {
+ format: 'cjs',
+ fixedExtension: false,
+ outDir: './dist',
+ entry: ['./src/**/*.{ts,tsx,js,jsx}', '!./src/**/*.test.{ts,tsx,js}', '!./src/**/__tests__/**'],
+ unbundle: true,
+ clean: true,
+ minify: false,
+ sourcemap: true,
+ deps: {
+ // Keep relative import specifiers unchanged so Metro can apply platform-specific resolution.
+ neverBundle: preserveRelativeImports,
+ },
+ define: {
+ __DEV__: `${isWatch}`,
+ },
+ };
+
+ return runAfterLast(['pnpm build:declarations', shouldPublish && 'pkglab pub --ping'])(options);
+});
diff --git a/packages/expo-native/vitest.config.mts b/packages/expo-native/vitest.config.mts
new file mode 100644
index 00000000000..20bc22a33db
--- /dev/null
+++ b/packages/expo-native/vitest.config.mts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ plugins: [],
+ test: {
+ environment: 'jsdom',
+ includeSource: ['**/*.{js,ts,jsx,tsx}'],
+ setupFiles: './vitest.setup.mts',
+ },
+});
diff --git a/packages/expo-native/vitest.setup.mts b/packages/expo-native/vitest.setup.mts
new file mode 100644
index 00000000000..283c324f550
--- /dev/null
+++ b/packages/expo-native/vitest.setup.mts
@@ -0,0 +1,22 @@
+import { cleanup } from '@testing-library/react';
+import { afterEach, beforeAll, vi } from 'vitest';
+
+// Mock globalThis.expo for expo-modules-core
+if (!globalThis.expo) {
+ // @ts-expect-error - Mocking expo for tests
+ globalThis.expo = {
+ EventEmitter: vi.fn(),
+ };
+}
+
+// Define __DEV__ for expo-modules-core
+if (typeof globalThis.__DEV__ === 'undefined') {
+ // @ts-expect-error - Mocking __DEV__ for tests
+ globalThis.__DEV__ = false;
+}
+
+beforeAll(() => {});
+
+afterEach(() => {
+ cleanup();
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index be2b32a7f6a..39ddc5fae02 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -634,6 +634,9 @@ importers:
'@clerk/clerk-js':
specifier: workspace:^
version: link:../clerk-js
+ '@clerk/expo-native':
+ specifier: workspace:>=0.0.1
+ version: link:../expo-native
'@clerk/react':
specifier: workspace:^
version: link:../react
@@ -708,6 +711,28 @@ importers:
specifier: ~54.0.36
version: 54.0.36(@babel/core@7.29.7)(bufferutil@4.1.0)(graphql@16.14.1)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@12.3.7(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@18.3.28)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ packages/expo-native:
+ dependencies:
+ expo:
+ specifier: catalog:peer-expo
+ version: 54.0.36(@babel/core@7.29.7)(bufferutil@4.1.0)(graphql@16.14.1)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@12.3.7(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@18.3.28)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ react:
+ specifier: 18.3.1
+ version: 18.3.1
+ tslib:
+ specifier: catalog:repo
+ version: 2.8.1
+ devDependencies:
+ '@clerk/expo':
+ specifier: workspace:*
+ version: link:../expo
+ '@expo/config-plugins':
+ specifier: ^54.0.4
+ version: 54.0.5
+ react-native:
+ specifier: ^0.86.0
+ version: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@12.3.7(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@18.3.28)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)
+
packages/expo-passkeys:
dependencies:
'@clerk/shared':
From 170964735a8569f1898b4d5dca061761160324f4 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:28:13 -0400
Subject: [PATCH 3/8] feat(expo): re-export native components from
@clerk/expo-native and delegate its config plugin
Co-Authored-By: Claude Opus 5.5 (1M context)
---
package.json | 2 +-
packages/expo/app.plugin.js | 156 ++++++++++++++++++
packages/expo/native/index.d.ts | 1 +
packages/expo/native/package.json | 2 +-
packages/expo/package.json | 16 +-
packages/expo/src/__tests__/appPlugin.test.js | 120 ++++++++++++++
.../__tests__/useBiometricCredentials.test.ts | 19 ++-
.../useBiometricCredentials.shared.ts | 15 +-
.../expo/src/native/__tests__/index.test.ts | 57 +++++++
packages/expo/src/native/index.ts | 27 +++
.../expo/src/native/loadClerkExpoNative.ts | 18 ++
.../expo/src/provider/nativeClientSync.tsx | 2 +-
12 files changed, 416 insertions(+), 19 deletions(-)
create mode 100644 packages/expo/app.plugin.js
create mode 100644 packages/expo/native/index.d.ts
create mode 100644 packages/expo/src/__tests__/appPlugin.test.js
create mode 100644 packages/expo/src/native/__tests__/index.test.ts
create mode 100644 packages/expo/src/native/index.ts
create mode 100644 packages/expo/src/native/loadClerkExpoNative.ts
diff --git a/package.json b/package.json
index 5884e61374c..7620564b4fd 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"changeset": "changeset",
"changeset:empty": "pnpm changeset --empty",
"clean": "turbo run clean",
- "dev": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/* --filter=!@clerk/expo --filter=!@clerk/tanstack-react-start --filter=!@clerk/chrome-extension",
+ "dev": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/* --filter=!@clerk/expo --filter=!@clerk/expo-native --filter=!@clerk/tanstack-react-start --filter=!@clerk/chrome-extension",
"dev:fe-libs": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/clerk-js --filter=@clerk/ui --filter=@clerk/shared",
"dev:js": "TURBO_UI=0 FORCE_COLOR=1 turbo dev:current --filter=@clerk/clerk-js",
"dev:sandbox": "TURBO_UI=0 FORCE_COLOR=1 turbo dev:sandbox:serve",
diff --git a/packages/expo/app.plugin.js b/packages/expo/app.plugin.js
new file mode 100644
index 00000000000..fbfa61128bd
--- /dev/null
+++ b/packages/expo/app.plugin.js
@@ -0,0 +1,156 @@
+/**
+ * Expo config plugin for @clerk/expo
+ *
+ * When this plugin is used:
+ * 1. Android registers the hosted auth callback intent filter
+ * 2. iOS gets the Sign in with Apple entitlement and, when configured, the Face ID usage description
+ * 3. If @clerk/expo-native is installed, its config plugin is applied for the Clerk native SDKs
+ */
+const { AndroidConfig, withAndroidManifest, withEntitlementsPlist, withInfoPlist } = require('@expo/config-plugins');
+
+const CLERK_EXPO_NATIVE = '@clerk/expo-native';
+const CLERK_EXPO_NATIVE_OPTIONS = ['keychainService', 'theme'];
+
+const addHostedAuthIntentFilter = (mainActivity, packageName) => {
+ const callbackHost = `${packageName}.hosted-callback`;
+ const intentFilters = mainActivity['intent-filter'] || [];
+ const hasAndroidName = (entries, name) => entries?.some(entry => entry.$?.['android:name'] === name);
+ const callbackIsRegistered = intentFilters.some(
+ intentFilter =>
+ hasAndroidName(intentFilter.action, 'android.intent.action.VIEW') &&
+ hasAndroidName(intentFilter.category, 'android.intent.category.DEFAULT') &&
+ hasAndroidName(intentFilter.category, 'android.intent.category.BROWSABLE') &&
+ intentFilter.data?.some(
+ data => data.$?.['android:scheme'] === 'clerk' && data.$?.['android:host'] === callbackHost,
+ ),
+ );
+
+ if (callbackIsRegistered) {
+ return;
+ }
+
+ intentFilters.push({
+ action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
+ category: [
+ { $: { 'android:name': 'android.intent.category.DEFAULT' } },
+ { $: { 'android:name': 'android.intent.category.BROWSABLE' } },
+ ],
+ data: [{ $: { 'android:scheme': 'clerk', 'android:host': callbackHost } }],
+ });
+ mainActivity['intent-filter'] = intentFilters;
+};
+
+const withClerkHostedAuthCallback = config => {
+ return withAndroidManifest(config, modConfig => {
+ const packageName = config.android?.package;
+ if (packageName) {
+ const mainActivity = AndroidConfig.Manifest.getMainActivityOrThrow(modConfig.modResults);
+ addHostedAuthIntentFilter(mainActivity, packageName);
+ }
+ return modConfig;
+ });
+};
+
+const withClerkFaceIDPermission = (config, { faceIDPermission } = {}) => {
+ if (faceIDPermission === undefined) {
+ return config;
+ }
+
+ if (typeof faceIDPermission !== 'string' || faceIDPermission.trim().length === 0) {
+ throw new Error('Clerk: faceIDPermission must be a non-empty string');
+ }
+
+ return withInfoPlist(config, modConfig => {
+ if (!Object.hasOwn(modConfig.modResults, 'NSFaceIDUsageDescription')) {
+ modConfig.modResults.NSFaceIDUsageDescription = faceIDPermission;
+ }
+ return modConfig;
+ });
+};
+
+/**
+ * Add Sign in with Apple entitlement to the iOS app.
+ * Required for the native Apple Sign In flow via ASAuthorizationController.
+ */
+const withClerkAppleSignIn = config => {
+ return withEntitlementsPlist(config, modConfig => {
+ if (!modConfig.modResults['com.apple.developer.applesignin']) {
+ modConfig.modResults['com.apple.developer.applesignin'] = ['Default'];
+ console.log('✅ Added Sign in with Apple entitlement');
+ }
+ return modConfig;
+ });
+};
+
+const resolveClerkExpoNativePlugin = config => {
+ try {
+ const paths = [config._internal?.projectRoot, process.cwd()].filter(Boolean);
+ return require(require.resolve(`${CLERK_EXPO_NATIVE}/app.plugin.js`, { paths }));
+ } catch {
+ return null;
+ }
+};
+
+const getListedPluginProps = (config, name) => {
+ for (const entry of config.plugins || []) {
+ if (entry === name) {
+ return {};
+ }
+ if (Array.isArray(entry) && entry[0] === name) {
+ return entry[1] || {};
+ }
+ }
+ return {};
+};
+
+/**
+ * Apply the @clerk/expo-native config plugin when it is installed, so apps that only list
+ * "@clerk/expo" keep the iOS deployment target and native SDK configuration they need.
+ */
+const withClerkExpoNative = (config, props = {}, resolvePlugin = resolveClerkExpoNativePlugin) => {
+ const nativeProps = Object.fromEntries(
+ CLERK_EXPO_NATIVE_OPTIONS.filter(option => props[option] !== undefined).map(option => [option, props[option]]),
+ );
+ const nativeOptionNames = Object.keys(nativeProps)
+ .map(option => `"${option}"`)
+ .join(', ');
+
+ if (config._internal?.pluginHistory?.[CLERK_EXPO_NATIVE]) {
+ if (nativeOptionNames) {
+ console.warn(
+ `⚠️ Clerk: ${nativeOptionNames} passed to the "@clerk/expo" plugin will be ignored because the "${CLERK_EXPO_NATIVE}" plugin already ran. Pass them to the "${CLERK_EXPO_NATIVE}" plugin instead.`,
+ );
+ }
+ return config;
+ }
+
+ const clerkExpoNativePlugin = resolvePlugin(config);
+ if (!clerkExpoNativePlugin) {
+ if (nativeOptionNames) {
+ console.warn(
+ `⚠️ Clerk: ${nativeOptionNames} configure Clerk native components, which have moved to ${CLERK_EXPO_NATIVE}. Install it with \`npx expo install ${CLERK_EXPO_NATIVE}\` and add "${CLERK_EXPO_NATIVE}" to the plugins array in your app config.`,
+ );
+ }
+ return config;
+ }
+
+ return clerkExpoNativePlugin(config, { ...nativeProps, ...getListedPluginProps(config, CLERK_EXPO_NATIVE) });
+};
+
+const withClerkExpo = (config, props = {}) => {
+ const { appleSignIn = true } = props;
+ if (appleSignIn !== false) {
+ config = withClerkAppleSignIn(config);
+ }
+ config = withClerkHostedAuthCallback(config);
+ config = withClerkFaceIDPermission(config, props);
+ config = withClerkExpoNative(config, props);
+ return config;
+};
+
+module.exports = withClerkExpo;
+module.exports._testing = {
+ addHostedAuthIntentFilter,
+ withClerkExpoNative,
+ withClerkFaceIDPermission,
+};
diff --git a/packages/expo/native/index.d.ts b/packages/expo/native/index.d.ts
new file mode 100644
index 00000000000..0c04017d415
--- /dev/null
+++ b/packages/expo/native/index.d.ts
@@ -0,0 +1 @@
+export * from '@clerk/expo-native';
diff --git a/packages/expo/native/package.json b/packages/expo/native/package.json
index 6ae24b71af4..928da2cc7c8 100644
--- a/packages/expo/native/package.json
+++ b/packages/expo/native/package.json
@@ -1,4 +1,4 @@
{
"main": "../dist/native/index.js",
- "types": "../dist/native/index.d.ts"
+ "types": "./index.d.ts"
}
diff --git a/packages/expo/package.json b/packages/expo/package.json
index 95aa54c6c84..dc84ba82b93 100644
--- a/packages/expo/package.json
+++ b/packages/expo/package.json
@@ -30,7 +30,7 @@
},
"./app.plugin.js": "./app.plugin.js",
"./native": {
- "types": "./dist/native/index.d.ts",
+ "types": "./native/index.d.ts",
"default": "./dist/native/index.js"
},
"./web": {
@@ -85,8 +85,6 @@
"types": "./dist/index.d.ts",
"files": [
"dist",
- "android",
- "ios",
"native",
"web",
"local-credentials",
@@ -99,9 +97,6 @@
"hosted-auth",
"experimental",
"legacy",
- "src/specs",
- "expo-module.config.json",
- "react-native.config.js",
"app.plugin.js",
"app.plugin.d.ts"
],
@@ -142,6 +137,7 @@
},
"peerDependencies": {
"@clerk/expo-google-signin": ">=0.1.0",
+ "@clerk/expo-native": "workspace:>=0.0.1",
"@clerk/expo-passkeys": ">=0.0.6",
"expo": "catalog:peer-expo",
"expo-apple-authentication": ">=7.0.0",
@@ -159,6 +155,9 @@
"@clerk/expo-google-signin": {
"optional": true
},
+ "@clerk/expo-native": {
+ "optional": true
+ },
"@clerk/expo-passkeys": {
"optional": true
},
@@ -192,10 +191,5 @@
},
"publishConfig": {
"access": "public"
- },
- "codegenConfig": {
- "name": "ClerkExpoSpec",
- "type": "all",
- "jsSrcsDir": "src/specs"
}
}
diff --git a/packages/expo/src/__tests__/appPlugin.test.js b/packages/expo/src/__tests__/appPlugin.test.js
new file mode 100644
index 00000000000..78e46629533
--- /dev/null
+++ b/packages/expo/src/__tests__/appPlugin.test.js
@@ -0,0 +1,120 @@
+import { afterEach, describe, expect, test, vi } from 'vitest';
+
+// eslint-disable-next-line @typescript-eslint/no-require-imports -- CJS plugin, no ESM export
+const { withClerkExpoNative, withClerkFaceIDPermission } = require('../../app.plugin.js')._testing;
+
+function applyInfoPlistMod(config, modResults) {
+ return config.mods.ios.infoPlist({
+ ...config,
+ modRequest: {},
+ modResults,
+ });
+}
+
+describe('withClerkFaceIDPermission', () => {
+ test('adds the configured Face ID usage description', async () => {
+ const config = withClerkFaceIDPermission(
+ { name: 'test', slug: 'test' },
+ { faceIDPermission: 'Allow $(PRODUCT_NAME) to use Face ID for secure sign-in.' },
+ );
+
+ const result = await applyInfoPlistMod(config, {});
+
+ expect(result.modResults.NSFaceIDUsageDescription).toBe('Allow $(PRODUCT_NAME) to use Face ID for secure sign-in.');
+ });
+
+ test('preserves an app-provided Face ID usage description', async () => {
+ const config = withClerkFaceIDPermission(
+ { name: 'test', slug: 'test' },
+ { faceIDPermission: 'Clerk-provided description' },
+ );
+
+ const result = await applyInfoPlistMod(config, {
+ NSFaceIDUsageDescription: 'App-provided description',
+ });
+
+ expect(result.modResults.NSFaceIDUsageDescription).toBe('App-provided description');
+ });
+
+ test('does not configure the Info.plist without an explicit permission description', () => {
+ const config = { name: 'test', slug: 'test' };
+
+ expect(withClerkFaceIDPermission(config)).toBe(config);
+ expect(config).not.toHaveProperty('mods');
+ });
+
+ test.each([null, '', ' ', true])('rejects an invalid permission description: %j', faceIDPermission => {
+ expect(() => withClerkFaceIDPermission({ name: 'test', slug: 'test' }, { faceIDPermission })).toThrow(
+ 'faceIDPermission must be a non-empty string',
+ );
+ });
+});
+
+describe('withClerkExpoNative', () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ test('applies the @clerk/expo-native plugin with its options when it is installed', () => {
+ const nativePlugin = vi.fn(config => ({ ...config, applied: true }));
+ const config = { name: 'test', slug: 'test' };
+
+ const result = withClerkExpoNative(
+ config,
+ { keychainService: 'com.example.shared', theme: './theme.json', appleSignIn: false },
+ () => nativePlugin,
+ );
+
+ expect(result.applied).toBe(true);
+ expect(nativePlugin).toHaveBeenCalledWith(config, { keychainService: 'com.example.shared', theme: './theme.json' });
+ });
+
+ test('prefers options passed to an explicitly listed @clerk/expo-native plugin', () => {
+ const nativePlugin = vi.fn(config => config);
+ const config = {
+ name: 'test',
+ slug: 'test',
+ plugins: ['@clerk/expo', ['@clerk/expo-native', { theme: './native-theme.json' }]],
+ };
+
+ withClerkExpoNative(config, { keychainService: 'com.example.shared', theme: './theme.json' }, () => nativePlugin);
+
+ expect(nativePlugin).toHaveBeenCalledWith(config, {
+ keychainService: 'com.example.shared',
+ theme: './native-theme.json',
+ });
+ });
+
+ test('does not apply the @clerk/expo-native plugin twice', () => {
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
+ const nativePlugin = vi.fn(config => config);
+ const config = {
+ name: 'test',
+ slug: 'test',
+ _internal: { pluginHistory: { '@clerk/expo-native': { name: '@clerk/expo-native', version: '0.0.1' } } },
+ };
+
+ expect(withClerkExpoNative(config, {}, () => nativePlugin)).toBe(config);
+ expect(nativePlugin).not.toHaveBeenCalled();
+ expect(warn).not.toHaveBeenCalled();
+
+ withClerkExpoNative(config, { theme: './theme.json' }, () => nativePlugin);
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('Pass them to the "@clerk/expo-native" plugin instead'));
+ });
+
+ test('leaves the config untouched when @clerk/expo-native is not installed', () => {
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
+ const config = { name: 'test', slug: 'test' };
+
+ expect(withClerkExpoNative(config, {}, () => null)).toBe(config);
+ expect(warn).not.toHaveBeenCalled();
+ });
+
+ test('warns with install instructions when native-only options are passed without @clerk/expo-native', () => {
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
+ const config = { name: 'test', slug: 'test' };
+
+ expect(withClerkExpoNative(config, { keychainService: 'com.example.shared' }, () => null)).toBe(config);
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('npx expo install @clerk/expo-native'));
+ });
+});
diff --git a/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts b/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
index dee295ff1a5..1b7a7c46c5d 100644
--- a/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
+++ b/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
@@ -24,6 +24,7 @@ const mocks = vi.hoisted(() => ({
useClerk: vi.fn(),
setActive: vi.fn(),
synchronizeNativeClientToJs: vi.fn(),
+ isNativeModuleInstalled: true,
nativeModule: {
getTrustedDeviceAvailability: vi.fn(),
listTrustedDevices: vi.fn(),
@@ -38,7 +39,9 @@ vi.mock('@clerk/react', () => ({
}));
vi.mock('../../utils/native-module', () => ({
- ClerkExpoModule: mocks.nativeModule,
+ get ClerkExpoModule() {
+ return mocks.isNativeModuleInstalled ? mocks.nativeModule : null;
+ },
}));
vi.mock('react-native', () => ({
@@ -539,13 +542,25 @@ describe('useBiometricCredentials on iOS', () => {
});
});
+ test('explains how to install @clerk/expo-native when the native module is missing', async () => {
+ mocks.isNativeModuleInstalled = false;
+
+ try {
+ await expect(renderBiometricCredentials().signIn()).rejects.toThrow(
+ 'Biometric credentials require the @clerk/expo-native package in a development build. Install it with `npx expo install @clerk/expo-native`',
+ );
+ } finally {
+ mocks.isNativeModuleInstalled = true;
+ }
+ });
+
test('explains that the development client must contain the native methods', async () => {
const signInWithTrustedDevice = mocks.nativeModule.signInWithTrustedDevice;
Object.assign(mocks.nativeModule, { signInWithTrustedDevice: undefined });
try {
await expect(renderBiometricCredentials().signIn()).rejects.toThrow(
- 'Biometric credentials require a development build containing a compatible version of @clerk/expo.',
+ 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native.',
);
} finally {
Object.assign(mocks.nativeModule, { signInWithTrustedDevice });
diff --git a/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts b/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
index 3e92e5287be..49f611363f1 100644
--- a/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
+++ b/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
@@ -14,6 +14,9 @@ import type {
const DEFAULT_POLICY = 'biometry_current_set';
+const CLERK_EXPO_NATIVE_INSTALL_INSTRUCTIONS =
+ 'Install it with `npx expo install @clerk/expo-native`, add "@clerk/expo-native" to the plugins array in your app config, then rebuild your development build.';
+
function toBiometricCredentialPlatform(platform: string): BiometricCredentialPlatform {
return platform === 'ios' || platform === 'android' ? platform : 'unknown';
}
@@ -25,15 +28,21 @@ function toBiometricCredentialStatus(status: string): BiometricCredentialStatus
function getNativeModule(): NativeBiometricCredentialModule {
const nativeModule = ClerkExpoModule;
+ if (!nativeModule) {
+ return errorThrower.throw(
+ `Biometric credentials require the @clerk/expo-native package in a development build. ${CLERK_EXPO_NATIVE_INSTALL_INSTRUCTIONS}`,
+ );
+ }
+
if (
- !nativeModule?.getTrustedDeviceAvailability ||
+ !nativeModule.getTrustedDeviceAvailability ||
!nativeModule.listTrustedDevices ||
!nativeModule.enrollTrustedDevice ||
!nativeModule.revokeTrustedDevice ||
!nativeModule.signInWithTrustedDevice
) {
return errorThrower.throw(
- 'Biometric credentials require a development build containing a compatible version of @clerk/expo.',
+ 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native.',
);
}
@@ -86,7 +95,7 @@ function createBiometricCredentials(clerk: ReturnType): UseBiom
const nativeModule = getNativeModule();
if (typeof nativeModule.reverifyWithBiometrics !== 'function') {
return errorThrower.throw(
- 'Biometric reverification requires a development build containing a compatible version of @clerk/expo.',
+ 'Biometric reverification requires a development build containing a compatible version of @clerk/expo-native.',
);
}
const level = params?.level ?? 'first_factor';
diff --git a/packages/expo/src/native/__tests__/index.test.ts b/packages/expo/src/native/__tests__/index.test.ts
new file mode 100644
index 00000000000..6d936310043
--- /dev/null
+++ b/packages/expo/src/native/__tests__/index.test.ts
@@ -0,0 +1,57 @@
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+
+const importNativeEntry = () => import('../index');
+
+describe('@clerk/expo/native', () => {
+ afterEach(() => {
+ vi.doUnmock('../loadClerkExpoNative');
+ vi.resetModules();
+ vi.restoreAllMocks();
+ });
+
+ describe('when @clerk/expo-native is not installed', () => {
+ beforeEach(() => {
+ vi.doMock('../loadClerkExpoNative', () => ({ loadClerkExpoNative: () => null }));
+ });
+
+ test.each(['AuthView', 'UserButton', 'UserProfileView'] as const)(
+ 'rendering %s throws an error with install instructions',
+ async name => {
+ const Component = (await importNativeEntry())[name] as (props: object) => unknown;
+
+ expect(() => Component({})).toThrow(
+ /`.+` is unavailable\. Native components have moved to the @clerk\/expo-native package\. Install it with `npx expo install @clerk\/expo-native`/,
+ );
+ },
+ );
+
+ test.each(['useAuthViewState', 'useUserProfileCustomPageNavigation'] as const)(
+ 'calling %s throws an error with install instructions',
+ async name => {
+ const hook = (await importNativeEntry())[name] as () => unknown;
+
+ expect(() => hook()).toThrow(`\`${name}\` is unavailable.`);
+ expect(() => hook()).toThrow('add "@clerk/expo-native" to the plugins array in your app config');
+ },
+ );
+ });
+
+ test('re-exports @clerk/expo-native when it is installed', async () => {
+ const clerkExpoNative = {
+ AuthView: vi.fn(() => null),
+ UserButton: vi.fn(() => null),
+ UserProfileView: vi.fn(() => null),
+ useAuthViewState: vi.fn(),
+ useUserProfileCustomPageNavigation: vi.fn(),
+ };
+ vi.doMock('../loadClerkExpoNative', () => ({ loadClerkExpoNative: () => clerkExpoNative }));
+
+ const nativeEntry = await importNativeEntry();
+
+ expect(nativeEntry.AuthView).toBe(clerkExpoNative.AuthView);
+ expect(nativeEntry.UserButton).toBe(clerkExpoNative.UserButton);
+ expect(nativeEntry.UserProfileView).toBe(clerkExpoNative.UserProfileView);
+ expect(nativeEntry.useAuthViewState).toBe(clerkExpoNative.useAuthViewState);
+ expect(nativeEntry.useUserProfileCustomPageNavigation).toBe(clerkExpoNative.useUserProfileCustomPageNavigation);
+ });
+});
diff --git a/packages/expo/src/native/index.ts b/packages/expo/src/native/index.ts
new file mode 100644
index 00000000000..34cc5902993
--- /dev/null
+++ b/packages/expo/src/native/index.ts
@@ -0,0 +1,27 @@
+import { errorThrower } from '../errorThrower';
+import type { ClerkExpoNativeModule } from './loadClerkExpoNative';
+import { loadClerkExpoNative } from './loadClerkExpoNative';
+
+// Public types are declared in native/index.d.ts, which re-exports them from @clerk/expo-native.
+
+const CLERK_EXPO_NATIVE_MISSING_MESSAGE =
+ 'Native components have moved to the @clerk/expo-native package. ' +
+ 'Install it with `npx expo install @clerk/expo-native`, add "@clerk/expo-native" to the plugins array in your app config, ' +
+ 'then rebuild your native app. You can then import them from "@clerk/expo-native".';
+
+const clerkExpoNative = loadClerkExpoNative();
+
+function resolveExport(name: K) {
+ return (
+ clerkExpoNative?.[name] ??
+ function ClerkExpoNativeMissing(): never {
+ return errorThrower.throw(`\`${name}\` is unavailable. ${CLERK_EXPO_NATIVE_MISSING_MESSAGE}`);
+ }
+ );
+}
+
+export const AuthView = resolveExport('AuthView');
+export const UserButton = resolveExport('UserButton');
+export const UserProfileView = resolveExport('UserProfileView');
+export const useAuthViewState = resolveExport('useAuthViewState');
+export const useUserProfileCustomPageNavigation = resolveExport('useUserProfileCustomPageNavigation');
diff --git a/packages/expo/src/native/loadClerkExpoNative.ts b/packages/expo/src/native/loadClerkExpoNative.ts
new file mode 100644
index 00000000000..06f8e66b9dc
--- /dev/null
+++ b/packages/expo/src/native/loadClerkExpoNative.ts
@@ -0,0 +1,18 @@
+type ClerkExpoNativeExport = (...args: never[]) => unknown;
+
+export type ClerkExpoNativeModule = Partial<
+ Record<
+ 'AuthView' | 'UserButton' | 'UserProfileView' | 'useAuthViewState' | 'useUserProfileCustomPageNavigation',
+ ClerkExpoNativeExport
+ >
+>;
+
+export function loadClerkExpoNative(): ClerkExpoNativeModule | null {
+ try {
+ // Synchronous require() in try/catch so Metro treats @clerk/expo-native as an optional dependency.
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ return require('@clerk/expo-native') as ClerkExpoNativeModule;
+ } catch {
+ return null;
+ }
+}
diff --git a/packages/expo/src/provider/nativeClientSync.tsx b/packages/expo/src/provider/nativeClientSync.tsx
index 5f40ed5287c..99adbcd6e38 100644
--- a/packages/expo/src/provider/nativeClientSync.tsx
+++ b/packages/expo/src/provider/nativeClientSync.tsx
@@ -1163,7 +1163,7 @@ export function useNativeClientBootstrap({
if (__DEV__) {
console.debug(
`[ClerkProvider] Native Clerk module not available. ` +
- `To enable native features, add "@clerk/expo" to your app.json plugins array.`,
+ `To enable native features, install @clerk/expo-native and add "@clerk/expo-native" to your app.json plugins array.`,
);
}
} else if (__DEV__) {
From a01e01c7a9027439657529ec495f89df5c332938 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:28:14 -0400
Subject: [PATCH 4/8] chore(expo-native): wire the package into CI, the native
fixture, and add changesets
Co-Authored-By: Claude Opus 5.5 (1M context)
---
.changeset/expo-native-components-moved.md | 25 +++++++++++++++++++
.changeset/introduce-expo-native.md | 5 ++++
.../clerk-monorepo/references/package-map.md | 1 +
.github/labeler.yml | 4 +++
.github/workflows/api-changes.yml | 2 ++
.github/workflows/expo-native-build.yml | 10 +++++---
integration/templates/expo-native/app.json | 2 +-
7 files changed, 45 insertions(+), 4 deletions(-)
create mode 100644 .changeset/expo-native-components-moved.md
create mode 100644 .changeset/introduce-expo-native.md
diff --git a/.changeset/expo-native-components-moved.md b/.changeset/expo-native-components-moved.md
new file mode 100644
index 00000000000..6cf1fedf844
--- /dev/null
+++ b/.changeset/expo-native-components-moved.md
@@ -0,0 +1,25 @@
+---
+'@clerk/expo': minor
+---
+
+The native components (`AuthView`, `UserButton`, `UserProfileView`), the native module behind biometric credentials, and native client sync have moved to the new `@clerk/expo-native` package. Apps that don't install it no longer include the Clerk iOS and Android SDKs and no longer require iOS 17.
+
+If you use the native components or `useBiometricCredentials()`, install the new package:
+
+```sh
+npx expo install @clerk/expo-native
+```
+
+add its config plugin alongside `@clerk/expo` in your app config:
+
+```json
+{
+ "expo": {
+ "plugins": ["@clerk/expo", "@clerk/expo-native"]
+ }
+}
+```
+
+then rebuild your native app. `@clerk/expo/native` keeps working and re-exports the components from `@clerk/expo-native`, which you can also import from directly. Without `@clerk/expo-native` installed, rendering a component from `@clerk/expo/native` throws an error explaining how to install it.
+
+The `keychainService` and `theme` config plugin options now belong to the `@clerk/expo-native` plugin. The `@clerk/expo` plugin forwards them when `@clerk/expo-native` is installed, and warns otherwise.
diff --git a/.changeset/introduce-expo-native.md b/.changeset/introduce-expo-native.md
new file mode 100644
index 00000000000..637d416fff9
--- /dev/null
+++ b/.changeset/introduce-expo-native.md
@@ -0,0 +1,5 @@
+---
+'@clerk/expo-native': minor
+---
+
+Add `@clerk/expo-native`, the optional companion package to `@clerk/expo` that contains Clerk's prebuilt native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module built on the Clerk iOS and Android SDKs. It includes a config plugin that accepts the `keychainService` and `theme` options.
diff --git a/.claude/skills/clerk-monorepo/references/package-map.md b/.claude/skills/clerk-monorepo/references/package-map.md
index c5cabf3765b..b86fb4e5a44 100644
--- a/.claude/skills/clerk-monorepo/references/package-map.md
+++ b/.claude/skills/clerk-monorepo/references/package-map.md
@@ -34,6 +34,7 @@ backwards-compatibility contract (see `breaking-changes.md`).
| `@clerk/react-router` | adapter | | React Router v7 SDK. |
| `@clerk/tanstack-react-start` | adapter | | TanStack React Start SDK. |
| `@clerk/expo` | adapter | | React Native / Expo SDK. |
+| `@clerk/expo-native` | adapter | | Native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module for Expo, built on clerk-ios / clerk-android. Optional companion to `@clerk/expo`. |
| `@clerk/expo-passkeys` | adapter | | Passkeys companion library for Expo. |
| `@clerk/chrome-extension` | browser-runtime | | SDK for Chrome extension contexts. |
| `@clerk/localizations` | ui/i18n | | Translation strings for the UI components. Consumed by `ui`. |
diff --git a/.github/labeler.yml b/.github/labeler.yml
index b6f18eeb545..675d48e803f 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -28,6 +28,10 @@ expo:
- changed-files:
- any-glob-to-any-file: packages/expo/**
+expo-native:
+ - changed-files:
+ - any-glob-to-any-file: packages/expo-native/**
+
express:
- changed-files:
- any-glob-to-any-file: packages/express/**
diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml
index 25971a1ad1b..4d7ca8eda83 100644
--- a/.github/workflows/api-changes.yml
+++ b/.github/workflows/api-changes.yml
@@ -19,6 +19,7 @@ on:
- 'packages/clerk-js/**'
- 'packages/expo/**'
- 'packages/expo-google-signin/**'
+ - 'packages/expo-native/**'
- 'packages/expo-passkeys/**'
- 'packages/express/**'
- 'packages/fastify/**'
@@ -56,6 +57,7 @@ env:
--filter=@clerk/clerk-js
--filter=@clerk/expo
--filter=@clerk/expo-google-signin
+ --filter=@clerk/expo-native
--filter=@clerk/expo-passkeys
--filter=@clerk/express
--filter=@clerk/fastify
diff --git a/.github/workflows/expo-native-build.yml b/.github/workflows/expo-native-build.yml
index bf96461fe88..609392e24a5 100644
--- a/.github/workflows/expo-native-build.yml
+++ b/.github/workflows/expo-native-build.yml
@@ -11,6 +11,7 @@ on:
- 'integration/tests/expo-native/**'
- 'packages/expo/**'
- 'packages/expo-google-signin/**'
+ - 'packages/expo-native/**'
workflow_dispatch:
permissions:
@@ -85,6 +86,7 @@ jobs:
packages/clerk-js \
packages/expo \
packages/expo-google-signin \
+ packages/expo-native \
packages/react \
packages/shared \
"$FIXTURE_DIR" |
@@ -121,10 +123,11 @@ jobs:
- name: Build and pack Clerk packages
if: steps.native-build-cache.outputs.cache-hit != 'true'
run: |
- pnpm --filter @clerk/expo... build
+ pnpm turbo build --filter=@clerk/expo-native...
mkdir -p "$SDK_PACK_DIR"
pnpm --filter @clerk/expo pack --pack-destination "$SDK_PACK_DIR"
pnpm --filter @clerk/expo-google-signin pack --pack-destination "$SDK_PACK_DIR"
+ pnpm --filter @clerk/expo-native pack --pack-destination "$SDK_PACK_DIR"
- name: Install fixture dependencies
if: steps.native-build-cache.outputs.cache-hit != 'true'
@@ -135,10 +138,11 @@ jobs:
run: |
cp "package.sdk-$EXPO_SDK.json" package.json
pnpm install --no-frozen-lockfile
- # [0-9] keeps this glob off the clerk-expo-google-signin tarball.
+ # [0-9] keeps this glob off the clerk-expo-google-signin and clerk-expo-native tarballs.
SDK_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-[0-9]*.tgz)"
GOOGLE_SIGNIN_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-google-signin-*.tgz)"
- pnpm add "$SDK_TARBALL" "$GOOGLE_SIGNIN_TARBALL" -w
+ NATIVE_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-native-*.tgz)"
+ pnpm add "$SDK_TARBALL" "$GOOGLE_SIGNIN_TARBALL" "$NATIVE_TARBALL" -w
# expo-dev-client makes even release builds boot into the dev
# launcher (unreachable Metro in CI), which stalls every Maestro
# flow on a blank screen. Skip it on e2e jobs only.
diff --git a/integration/templates/expo-native/app.json b/integration/templates/expo-native/app.json
index 58e258ea523..4ea4b82bb8f 100644
--- a/integration/templates/expo-native/app.json
+++ b/integration/templates/expo-native/app.json
@@ -12,6 +12,6 @@
"android": {
"package": "com.clerk.exponativebuildfixture"
},
- "plugins": ["expo-secure-store", "@clerk/expo", "expo-web-browser"]
+ "plugins": ["expo-secure-store", "@clerk/expo", "@clerk/expo-native", "expo-web-browser"]
}
}
From d3be19af0b3214b63dc70ef2a2562bb1b288fc06 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:31:42 -0400
Subject: [PATCH 5/8] fix(expo): clarify config plugin warnings for
@clerk/expo-native options
Co-Authored-By: Claude Opus 5.5 (1M context)
---
packages/expo/app.plugin.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/expo/app.plugin.js b/packages/expo/app.plugin.js
index fbfa61128bd..29c276c2067 100644
--- a/packages/expo/app.plugin.js
+++ b/packages/expo/app.plugin.js
@@ -118,7 +118,7 @@ const withClerkExpoNative = (config, props = {}, resolvePlugin = resolveClerkExp
if (config._internal?.pluginHistory?.[CLERK_EXPO_NATIVE]) {
if (nativeOptionNames) {
console.warn(
- `⚠️ Clerk: ${nativeOptionNames} passed to the "@clerk/expo" plugin will be ignored because the "${CLERK_EXPO_NATIVE}" plugin already ran. Pass them to the "${CLERK_EXPO_NATIVE}" plugin instead.`,
+ `⚠️ Clerk: The following "@clerk/expo" plugin options are ignored because the "${CLERK_EXPO_NATIVE}" plugin already ran: ${nativeOptionNames}. Pass them to the "${CLERK_EXPO_NATIVE}" plugin instead.`,
);
}
return config;
@@ -128,7 +128,7 @@ const withClerkExpoNative = (config, props = {}, resolvePlugin = resolveClerkExp
if (!clerkExpoNativePlugin) {
if (nativeOptionNames) {
console.warn(
- `⚠️ Clerk: ${nativeOptionNames} configure Clerk native components, which have moved to ${CLERK_EXPO_NATIVE}. Install it with \`npx expo install ${CLERK_EXPO_NATIVE}\` and add "${CLERK_EXPO_NATIVE}" to the plugins array in your app config.`,
+ `⚠️ Clerk: The following "@clerk/expo" plugin options require ${CLERK_EXPO_NATIVE} and are ignored: ${nativeOptionNames}. Install it with \`npx expo install ${CLERK_EXPO_NATIVE}\` and add "${CLERK_EXPO_NATIVE}" to the plugins array in your app config.`,
);
}
return config;
From dd05e528852594db6241bbfc0ae715b510a9ec10 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Sun, 27 Sep 2026 10:38:50 -0400
Subject: [PATCH 6/8] refactor(expo-native): drop the dependency on @clerk/expo
@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)
---
.github/workflows/expo-native-build.yml | 2 +-
packages/expo-native/android/build.gradle | 19 ++-----
packages/expo-native/app.plugin.js | 44 ++++++++++++----
packages/expo-native/package.json | 3 +-
.../src/__tests__/appPlugin.test.js | 50 +++++++++++++++++++
.../src/__tests__/useAuthViewState.test.tsx | 2 +-
packages/expo-native/src/useAuthViewState.ts | 2 +-
packages/expo/package.json | 3 +-
pnpm-lock.yaml | 12 ++---
9 files changed, 98 insertions(+), 39 deletions(-)
create mode 100644 packages/expo-native/src/__tests__/appPlugin.test.js
diff --git a/.github/workflows/expo-native-build.yml b/.github/workflows/expo-native-build.yml
index 609392e24a5..375d3cc47d1 100644
--- a/.github/workflows/expo-native-build.yml
+++ b/.github/workflows/expo-native-build.yml
@@ -123,7 +123,7 @@ jobs:
- name: Build and pack Clerk packages
if: steps.native-build-cache.outputs.cache-hit != 'true'
run: |
- pnpm turbo build --filter=@clerk/expo-native...
+ pnpm --filter @clerk/expo... build
mkdir -p "$SDK_PACK_DIR"
pnpm --filter @clerk/expo pack --pack-destination "$SDK_PACK_DIR"
pnpm --filter @clerk/expo-google-signin pack --pack-destination "$SDK_PACK_DIR"
diff --git a/packages/expo-native/android/build.gradle b/packages/expo-native/android/build.gradle
index 5cb80b92dd8..18c81a17cbe 100644
--- a/packages/expo-native/android/build.gradle
+++ b/packages/expo-native/android/build.gradle
@@ -13,22 +13,9 @@ applyKotlinExpoModulesCorePlugin()
group = 'com.clerk.expo'
version = '1.0.0'
-// The native SDKs report the @clerk/expo version in the x-clerk-host-sdk-version header.
-def resolveClerkExpoVersion() {
- try {
- def process = new ProcessBuilder("node", "--print", "require.resolve('@clerk/expo/package.json')")
- .directory(projectDir)
- .start()
- def clerkExpoPackageJsonPath = process.inputStream.text.trim()
- if (process.waitFor() == 0 && clerkExpoPackageJsonPath) {
- return new JsonSlurper().parse(new File(clerkExpoPackageJsonPath)).version.toString()
- }
- } catch (Exception ignored) {
- }
- return new JsonSlurper().parse(new File(projectDir, "../package.json")).version.toString()
-}
-
-def clerkExpoVersion = resolveClerkExpoVersion()
+// Set by the @clerk/expo-native config plugin to the installed @clerk/expo version.
+def clerkExpoVersion = rootProject.findProperty("clerkExpo.hostSdkVersion")?.toString() ?:
+ new JsonSlurper().parse(new File(projectDir, "../package.json")).version.toString()
// Dependency versions - centralized for easier updates
// See: https://docs.gradle.org/current/userguide/version_catalogs.html for app-level version catalogs
diff --git a/packages/expo-native/app.plugin.js b/packages/expo-native/app.plugin.js
index f8faa236dea..98a22f4b09d 100644
--- a/packages/expo-native/app.plugin.js
+++ b/packages/expo-native/app.plugin.js
@@ -14,24 +14,29 @@ const {
withDangerousMod,
withInfoPlist,
withAppBuildGradle,
+ withGradleProperties,
} = require('@expo/config-plugins');
const path = require('path');
const fs = require('fs');
const packageJson = require('./package.json');
const CLERK_MIN_IOS_VERSION = '17.0';
+const HOST_SDK_VERSION_GRADLE_PROPERTY = 'clerkExpo.hostSdkVersion';
-// The native SDKs report the @clerk/expo version in the x-clerk-host-sdk-version header.
-const resolveHostSdkVersion = projectRoot => {
+const resolveClerkExpo = projectRoot => {
try {
- const paths = [projectRoot, process.cwd(), __dirname].filter(Boolean);
- return require(require.resolve('@clerk/expo/package.json', { paths })).version;
+ const paths = [projectRoot, process.cwd()].filter(Boolean);
+ const clerkExpoPackageJsonPath = require.resolve('@clerk/expo/package.json', { paths });
+ return {
+ dir: path.dirname(clerkExpoPackageJsonPath),
+ version: require(clerkExpoPackageJsonPath).version,
+ };
} catch {
- return packageJson.version;
+ return null;
}
};
-const withClerkIOS = config => {
+const withClerkIOS = (config, hostSdkVersion) => {
console.log('✅ Clerk iOS plugin loaded');
// IMPORTANT: Set iOS deployment target in Podfile.properties.json BEFORE pod install
@@ -89,7 +94,6 @@ const withClerkIOS = config => {
return config;
});
- const hostSdkVersion = resolveHostSdkVersion(config._internal?.projectRoot);
config = withInfoPlist(config, modConfig => {
modConfig.modResults.ClerkExpoVersion = hostSdkVersion;
return modConfig;
@@ -102,9 +106,17 @@ const withClerkIOS = config => {
* Add packaging exclusions to Android app build.gradle to resolve
* duplicate META-INF file conflicts from clerk-android dependencies.
*/
-const withClerkAndroid = config => {
+const withClerkAndroid = (config, hostSdkVersion) => {
console.log('✅ Clerk Android plugin loaded');
+ config = withGradleProperties(config, modConfig => {
+ modConfig.modResults = modConfig.modResults.filter(
+ item => !(item.type === 'property' && item.key === HOST_SDK_VERSION_GRADLE_PROPERTY),
+ );
+ modConfig.modResults.push({ type: 'property', key: HOST_SDK_VERSION_GRADLE_PROPERTY, value: hostSdkVersion });
+ return modConfig;
+ });
+
return withAppBuildGradle(config, modConfig => {
let buildGradle = modConfig.modResults.contents;
@@ -294,9 +306,18 @@ const withClerkTheme = (config, props = {}) => {
return config;
};
-const withClerkExpoNative = (config, props = {}) => {
- config = withClerkIOS(config);
- config = withClerkAndroid(config);
+const withClerkExpoNative = (config, props = {}, resolve = resolveClerkExpo) => {
+ const clerkExpo = resolve(config._internal?.projectRoot);
+ if (clerkExpo && fs.existsSync(path.join(clerkExpo.dir, 'expo-module.config.json'))) {
+ throw new Error(
+ `Clerk: @clerk/expo@${clerkExpo.version} still bundles the Clerk native module, which conflicts with @clerk/expo-native. Upgrade @clerk/expo to a version that supports @clerk/expo-native.`,
+ );
+ }
+ // Native requests report the @clerk/expo version in the x-clerk-host-sdk-version header.
+ const hostSdkVersion = clerkExpo?.version ?? packageJson.version;
+
+ config = withClerkIOS(config, hostSdkVersion);
+ config = withClerkAndroid(config, hostSdkVersion);
config = withClerkKeychainService(config, props);
config = withClerkTheme(config, props);
return config;
@@ -304,6 +325,7 @@ const withClerkExpoNative = (config, props = {}) => {
module.exports = createRunOncePlugin(withClerkExpoNative, packageJson.name, packageJson.version);
module.exports._testing = {
+ withClerkExpoNative,
validateThemeJson,
isPlainObject,
VALID_COLOR_KEYS,
diff --git a/packages/expo-native/package.json b/packages/expo-native/package.json
index 25e1addf9d4..f732b7297da 100644
--- a/packages/expo-native/package.json
+++ b/packages/expo-native/package.json
@@ -56,15 +56,14 @@
"test:watch": "vitest watch"
},
"dependencies": {
+ "@clerk/react": "workspace:^",
"tslib": "catalog:repo"
},
"devDependencies": {
- "@clerk/expo": "workspace:*",
"@expo/config-plugins": "^54.0.4",
"react-native": "^0.86.0"
},
"peerDependencies": {
- "@clerk/expo": "workspace:^",
"expo": "catalog:peer-expo",
"react": "^18.0.0 || ^19.0.0",
"react-native": ">=0.75"
diff --git a/packages/expo-native/src/__tests__/appPlugin.test.js b/packages/expo-native/src/__tests__/appPlugin.test.js
new file mode 100644
index 00000000000..bb611cb434b
--- /dev/null
+++ b/packages/expo-native/src/__tests__/appPlugin.test.js
@@ -0,0 +1,50 @@
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+
+// eslint-disable-next-line @typescript-eslint/no-require-imports -- CJS plugin, no ESM export
+const { withClerkExpoNative } = require('../../app.plugin.js')._testing;
+
+const applyMod = (config, platform, mod, modResults) =>
+ config.mods[platform][mod]({ ...config, modRequest: {}, modResults });
+
+describe('withClerkExpoNative', () => {
+ let clerkExpoDir;
+
+ beforeEach(() => {
+ vi.spyOn(console, 'log').mockImplementation(() => {});
+ clerkExpoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'clerk-expo-'));
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ fs.rmSync(clerkExpoDir, { recursive: true, force: true });
+ });
+
+ test('reports the installed @clerk/expo version on iOS and Android', async () => {
+ const config = withClerkExpoNative({ name: 'test', slug: 'test' }, {}, () => ({
+ dir: clerkExpoDir,
+ version: '4.8.0',
+ }));
+
+ const infoPlist = await applyMod(config, 'ios', 'infoPlist', {});
+ const gradleProperties = await applyMod(config, 'android', 'gradleProperties', [
+ { type: 'property', key: 'clerkExpo.hostSdkVersion', value: '4.7.0' },
+ ]);
+
+ expect(infoPlist.modResults.ClerkExpoVersion).toBe('4.8.0');
+ expect(gradleProperties.modResults).toEqual([
+ { type: 'property', key: 'clerkExpo.hostSdkVersion', value: '4.8.0' },
+ ]);
+ });
+
+ test('throws when the installed @clerk/expo still bundles the native module', () => {
+ fs.writeFileSync(path.join(clerkExpoDir, 'expo-module.config.json'), '{}');
+
+ expect(() =>
+ withClerkExpoNative({ name: 'test', slug: 'test' }, {}, () => ({ dir: clerkExpoDir, version: '4.7.1' })),
+ ).toThrow('@clerk/expo@4.7.1 still bundles the Clerk native module');
+ });
+});
diff --git a/packages/expo-native/src/__tests__/useAuthViewState.test.tsx b/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
index aab5496325c..7b16d1816ef 100644
--- a/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
+++ b/packages/expo-native/src/__tests__/useAuthViewState.test.tsx
@@ -13,7 +13,7 @@ const mocks = vi.hoisted(() => ({
remove: vi.fn(),
}));
-vi.mock('@clerk/expo', () => ({
+vi.mock('@clerk/react', () => ({
useAuth: () => mocks.auth,
}));
diff --git a/packages/expo-native/src/useAuthViewState.ts b/packages/expo-native/src/useAuthViewState.ts
index a4c4a5f3b61..c32f8e3e637 100644
--- a/packages/expo-native/src/useAuthViewState.ts
+++ b/packages/expo-native/src/useAuthViewState.ts
@@ -1,4 +1,4 @@
-import { useAuth } from '@clerk/expo';
+import { useAuth } from '@clerk/react';
import { useEffect, useState } from 'react';
import type { NativeAuthFlowState } from './specs/NativeClerkModule.types';
diff --git a/packages/expo/package.json b/packages/expo/package.json
index dc84ba82b93..46deb108a47 100644
--- a/packages/expo/package.json
+++ b/packages/expo/package.json
@@ -122,6 +122,7 @@
},
"devDependencies": {
"@clerk/expo-google-signin": "workspace:*",
+ "@clerk/expo-native": "workspace:*",
"@clerk/expo-passkeys": "workspace:*",
"@expo/config-plugins": "^54.0.4",
"@types/base-64": "^1.0.2",
@@ -137,7 +138,7 @@
},
"peerDependencies": {
"@clerk/expo-google-signin": ">=0.1.0",
- "@clerk/expo-native": "workspace:>=0.0.1",
+ "@clerk/expo-native": ">=0.1.0",
"@clerk/expo-passkeys": ">=0.0.6",
"expo": "catalog:peer-expo",
"expo-apple-authentication": ">=7.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 39ddc5fae02..0b78f04fdd7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -634,9 +634,6 @@ importers:
'@clerk/clerk-js':
specifier: workspace:^
version: link:../clerk-js
- '@clerk/expo-native':
- specifier: workspace:>=0.0.1
- version: link:../expo-native
'@clerk/react':
specifier: workspace:^
version: link:../react
@@ -665,6 +662,9 @@ importers:
'@clerk/expo-google-signin':
specifier: workspace:*
version: link:../expo-google-signin
+ '@clerk/expo-native':
+ specifier: workspace:*
+ version: link:../expo-native
'@clerk/expo-passkeys':
specifier: workspace:*
version: link:../expo-passkeys
@@ -713,6 +713,9 @@ importers:
packages/expo-native:
dependencies:
+ '@clerk/react':
+ specifier: workspace:^
+ version: link:../react
expo:
specifier: catalog:peer-expo
version: 54.0.36(@babel/core@7.29.7)(bufferutil@4.1.0)(graphql@16.14.1)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@12.3.7(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@18.3.28)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)
@@ -723,9 +726,6 @@ importers:
specifier: catalog:repo
version: 2.8.1
devDependencies:
- '@clerk/expo':
- specifier: workspace:*
- version: link:../expo
'@expo/config-plugins':
specifier: ^54.0.4
version: 54.0.5
From 5ef3c7d6bf66ed751b163fd8eb0b5f1de25fd738 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Mon, 28 Sep 2026 15:34:21 -0400
Subject: [PATCH 7/8] chore(expo): leave biometric credentials out of the
native split changeset
Co-Authored-By: Claude Opus 5.5 (1M context)
---
.changeset/expo-native-components-moved.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.changeset/expo-native-components-moved.md b/.changeset/expo-native-components-moved.md
index 6cf1fedf844..5f4a79a356e 100644
--- a/.changeset/expo-native-components-moved.md
+++ b/.changeset/expo-native-components-moved.md
@@ -2,9 +2,9 @@
'@clerk/expo': minor
---
-The native components (`AuthView`, `UserButton`, `UserProfileView`), the native module behind biometric credentials, and native client sync have moved to the new `@clerk/expo-native` package. Apps that don't install it no longer include the Clerk iOS and Android SDKs and no longer require iOS 17.
+The native components (`AuthView`, `UserButton`, `UserProfileView`) and native client sync have moved to the new `@clerk/expo-native` package. Apps that don't install it no longer include the Clerk iOS and Android SDKs and no longer require iOS 17.
-If you use the native components or `useBiometricCredentials()`, install the new package:
+If you use the native components, install the new package:
```sh
npx expo install @clerk/expo-native
From 170d56f62d2b36b47b84110c1ee8cff67f47d026 Mon Sep 17 00:00:00 2001
From: Mike Pitre <12040919+mikepitre@users.noreply.github.com>
Date: Mon, 28 Sep 2026 16:08:47 -0400
Subject: [PATCH 8/8] refactor(expo-native-components): rename
@clerk/expo-native to @clerk/expo-native-components
Co-Authored-By: Claude Opus 5.5 (1M context)
---
.changeset/expo-native-components-moved.md | 10 ++--
.changeset/introduce-expo-native.md | 4 +-
.../clerk-monorepo/references/package-map.md | 54 +++++++++----------
.github/labeler.yml | 4 +-
.github/workflows/api-changes.yml | 4 +-
.github/workflows/expo-native-build.yml | 8 +--
package.json | 2 +-
.../.gitignore | 0
.../LICENSE | 0
.../README.md | 28 +++++-----
.../android/build.gradle | 4 +-
.../android/src/main/AndroidManifest.xml | 0
.../expo/modules/clerk/ClerkAuthViewModule.kt | 0
.../clerk/ClerkComposeNativeViewHost.kt | 0
.../expo/modules/clerk/ClerkExpoModule.kt | 0
.../clerk/ClerkUserButtonViewModule.kt | 0
.../clerk/ClerkUserProfileCustomPageState.kt | 0
.../clerk/ClerkUserProfileViewModule.kt | 0
.../clerk/BiometricCredentialBridgeTest.kt | 0
.../ClerkUserProfileCustomPageStateTest.kt | 0
.../expo-native-components/app.plugin.d.ts | 5 ++
.../app.plugin.js | 10 ++--
.../expo-module.config.json | 0
.../ios/ClerkAppDelegateSubscriber.swift | 0
.../ios/ClerkAuthNativeView.swift | 0
.../ios/ClerkExpo.podspec | 0
.../ios/ClerkExpoModule.swift | 0
.../ios/ClerkNativeBridge.swift | 0
.../ios/ClerkNativeViewHost.swift | 0
.../ios/ClerkUserButtonNativeView.swift | 0
.../ios/ClerkUserProfileNativeView.swift | 0
.../Tests/ClerkAuthNativeViewPaperTests.swift | 0
.../ios/Tests/ClerkNativeBridgeTests.swift | 0
.../ios/Tests/ClerkNativeViewHostTests.swift | 0
...ClerkUserProfileCustomPageStateTests.swift | 0
.../package.json | 4 +-
.../react-native.config.js | 0
.../src/AuthView.tsx | 4 +-
.../src/AuthView.types.ts | 0
.../src/EmbeddedNavigation.types.ts | 0
.../src/UserButton.tsx | 2 +-
.../src/UserProfileCustomPages.tsx | 0
.../src/UserProfileView.tsx | 4 +-
.../src/__tests__/AuthView.test.tsx | 0
.../src/__tests__/UserButton.test.tsx | 0
.../__tests__/UserProfileCustomPages.test.tsx | 0
.../src/__tests__/UserProfileView.test.tsx | 0
.../src/__tests__/appPlugin.test.js | 11 ++--
.../src/__tests__/appPlugin.theme.test.js | 0
.../src/__tests__/useAuthViewState.test.tsx | 0
.../src/index.ts | 6 +--
.../src/specs/NativeClerkAuthView.android.ts | 0
.../src/specs/NativeClerkAuthView.ts | 0
.../src/specs/NativeClerkModule.ts | 0
.../src/specs/NativeClerkModule.types.ts | 0
.../src/specs/NativeClerkModule.web.ts | 0
.../NativeClerkUserButtonView.android.ts | 0
.../src/specs/NativeClerkUserButtonView.ts | 0
.../NativeClerkUserProfileView.android.ts | 0
.../src/specs/NativeClerkUserProfileView.ts | 0
.../specs/__tests__/native-view.web.test.ts | 0
.../src/useAuthViewState.ts | 0
.../src/utils/native-module.ts | 0
.../tsconfig.declarations.json | 0
.../tsconfig.json | 0
.../tsconfig.test.json | 0
.../tsdown.config.mts | 0
.../vitest.config.mts | 0
.../vitest.setup.mts | 0
packages/expo-native/app.plugin.d.ts | 5 --
packages/expo/app.plugin.js | 12 ++---
packages/expo/native/index.d.ts | 2 +-
packages/expo/package.json | 6 +--
packages/expo/src/__tests__/appPlugin.test.js | 42 +++++++++------
.../__tests__/useBiometricCredentials.test.ts | 6 +--
.../useBiometricCredentials.shared.ts | 8 +--
.../expo/src/native/__tests__/index.test.ts | 8 +--
packages/expo/src/native/index.ts | 8 +--
.../expo/src/native/loadClerkExpoNative.ts | 4 +-
.../expo/src/provider/nativeClientSync.tsx | 2 +-
pnpm-lock.yaml | 6 +--
81 files changed, 142 insertions(+), 131 deletions(-)
rename packages/{expo-native => expo-native-components}/.gitignore (100%)
rename packages/{expo-native => expo-native-components}/LICENSE (100%)
rename packages/{expo-native => expo-native-components}/README.md (74%)
rename packages/{expo-native => expo-native-components}/android/build.gradle (96%)
rename packages/{expo-native => expo-native-components}/android/src/main/AndroidManifest.xml (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt (100%)
rename packages/{expo-native => expo-native-components}/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt (100%)
create mode 100644 packages/expo-native-components/app.plugin.d.ts
rename packages/{expo-native => expo-native-components}/app.plugin.js (96%)
rename packages/{expo-native => expo-native-components}/expo-module.config.json (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkAppDelegateSubscriber.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkAuthNativeView.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkExpo.podspec (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkExpoModule.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkNativeBridge.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkNativeViewHost.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkUserButtonNativeView.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/ClerkUserProfileNativeView.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/Tests/ClerkAuthNativeViewPaperTests.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/Tests/ClerkNativeBridgeTests.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/Tests/ClerkNativeViewHostTests.swift (100%)
rename packages/{expo-native => expo-native-components}/ios/Tests/ClerkUserProfileCustomPageStateTests.swift (100%)
rename packages/{expo-native => expo-native-components}/package.json (95%)
rename packages/{expo-native => expo-native-components}/react-native.config.js (100%)
rename packages/{expo-native => expo-native-components}/src/AuthView.tsx (92%)
rename packages/{expo-native => expo-native-components}/src/AuthView.types.ts (100%)
rename packages/{expo-native => expo-native-components}/src/EmbeddedNavigation.types.ts (100%)
rename packages/{expo-native => expo-native-components}/src/UserButton.tsx (98%)
rename packages/{expo-native => expo-native-components}/src/UserProfileCustomPages.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/UserProfileView.tsx (96%)
rename packages/{expo-native => expo-native-components}/src/__tests__/AuthView.test.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/__tests__/UserButton.test.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/__tests__/UserProfileCustomPages.test.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/__tests__/UserProfileView.test.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/__tests__/appPlugin.test.js (80%)
rename packages/{expo-native => expo-native-components}/src/__tests__/appPlugin.theme.test.js (100%)
rename packages/{expo-native => expo-native-components}/src/__tests__/useAuthViewState.test.tsx (100%)
rename packages/{expo-native => expo-native-components}/src/index.ts (87%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkAuthView.android.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkAuthView.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkModule.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkModule.types.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkModule.web.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkUserButtonView.android.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkUserButtonView.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkUserProfileView.android.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/NativeClerkUserProfileView.ts (100%)
rename packages/{expo-native => expo-native-components}/src/specs/__tests__/native-view.web.test.ts (100%)
rename packages/{expo-native => expo-native-components}/src/useAuthViewState.ts (100%)
rename packages/{expo-native => expo-native-components}/src/utils/native-module.ts (100%)
rename packages/{expo-native => expo-native-components}/tsconfig.declarations.json (100%)
rename packages/{expo-native => expo-native-components}/tsconfig.json (100%)
rename packages/{expo-native => expo-native-components}/tsconfig.test.json (100%)
rename packages/{expo-native => expo-native-components}/tsdown.config.mts (100%)
rename packages/{expo-native => expo-native-components}/vitest.config.mts (100%)
rename packages/{expo-native => expo-native-components}/vitest.setup.mts (100%)
delete mode 100644 packages/expo-native/app.plugin.d.ts
diff --git a/.changeset/expo-native-components-moved.md b/.changeset/expo-native-components-moved.md
index 5f4a79a356e..6c3fd2fc364 100644
--- a/.changeset/expo-native-components-moved.md
+++ b/.changeset/expo-native-components-moved.md
@@ -2,12 +2,12 @@
'@clerk/expo': minor
---
-The native components (`AuthView`, `UserButton`, `UserProfileView`) and native client sync have moved to the new `@clerk/expo-native` package. Apps that don't install it no longer include the Clerk iOS and Android SDKs and no longer require iOS 17.
+The native components (`AuthView`, `UserButton`, `UserProfileView`) and native client sync have moved to the new `@clerk/expo-native-components` package. Apps that don't install it no longer include the Clerk iOS and Android SDKs and no longer require iOS 17.
If you use the native components, install the new package:
```sh
-npx expo install @clerk/expo-native
+npx expo install @clerk/expo-native-components
```
add its config plugin alongside `@clerk/expo` in your app config:
@@ -15,11 +15,11 @@ add its config plugin alongside `@clerk/expo` in your app config:
```json
{
"expo": {
- "plugins": ["@clerk/expo", "@clerk/expo-native"]
+ "plugins": ["@clerk/expo", "@clerk/expo-native-components"]
}
}
```
-then rebuild your native app. `@clerk/expo/native` keeps working and re-exports the components from `@clerk/expo-native`, which you can also import from directly. Without `@clerk/expo-native` installed, rendering a component from `@clerk/expo/native` throws an error explaining how to install it.
+then rebuild your native app. `@clerk/expo/native` keeps working and re-exports the components from `@clerk/expo-native-components`, which you can also import from directly. Without `@clerk/expo-native-components` installed, rendering a component from `@clerk/expo/native` throws an error explaining how to install it.
-The `keychainService` and `theme` config plugin options now belong to the `@clerk/expo-native` plugin. The `@clerk/expo` plugin forwards them when `@clerk/expo-native` is installed, and warns otherwise.
+The `keychainService` and `theme` config plugin options now belong to the `@clerk/expo-native-components` plugin. The `@clerk/expo` plugin forwards them when `@clerk/expo-native-components` is installed, and warns otherwise.
diff --git a/.changeset/introduce-expo-native.md b/.changeset/introduce-expo-native.md
index 637d416fff9..a7a0839fb61 100644
--- a/.changeset/introduce-expo-native.md
+++ b/.changeset/introduce-expo-native.md
@@ -1,5 +1,5 @@
---
-'@clerk/expo-native': minor
+'@clerk/expo-native-components': minor
---
-Add `@clerk/expo-native`, the optional companion package to `@clerk/expo` that contains Clerk's prebuilt native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module built on the Clerk iOS and Android SDKs. It includes a config plugin that accepts the `keychainService` and `theme` options.
+Add `@clerk/expo-native-components`, the optional companion package to `@clerk/expo` that contains Clerk's prebuilt native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module built on the Clerk iOS and Android SDKs. It includes a config plugin that accepts the `keychainService` and `theme` options.
diff --git a/.claude/skills/clerk-monorepo/references/package-map.md b/.claude/skills/clerk-monorepo/references/package-map.md
index b86fb4e5a44..47abcecfa41 100644
--- a/.claude/skills/clerk-monorepo/references/package-map.md
+++ b/.claude/skills/clerk-monorepo/references/package-map.md
@@ -16,33 +16,33 @@ browser directly), **adapter** (framework SDK), **ui/i18n**, **tooling**. The "B
two packages whose runtime is pushed into apps pinned to older SDKs, so they carry the strict
backwards-compatibility contract (see `breaking-changes.md`).
-| Package | Category | BC | Purpose |
-| ----------------------------- | --------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `@clerk/shared` | foundational | | Internal utilities used by all SDKs (storage, events, React helpers). Hosts the shared types as `@clerk/shared/types`. Most-depended-on package. |
-| `@clerk/backend` | foundational | | Backend API REST client, JWT verification, webhook helpers. Used by every server adapter. |
-| `@clerk/clerk-js` | browser-runtime | ⚠️ | The browser runtime (script tag). Backwards-compat sensitive. |
-| `@clerk/ui` | ui | ⚠️ | React components for the hosted sign-in / sign-up flows (`packages/ui/src/components`). Consumed by the react/astro/vue/chrome-extension adapters. Backwards-compat sensitive. |
-| `@clerk/mosaic` | ui | | Experimental next-generation React components. Public ESM package (`UserButton` + `styles.css`). Ships unstyled, accessible primitives (dialog, menu, popover, ...) internally under `src/primitives/`. Reads Clerk context from the host SDK via `@clerk/shared`. |
-| `@clerk/react` | adapter (core) | | React hooks and context (`useAuth`, `useUser`, `useOrganization`, ...). Shared by the React-based adapters. |
-| `@clerk/nextjs` | adapter | | Next.js SDK: middleware, route handlers, server components. |
-| `@clerk/express` | adapter | | Express middleware and server helpers. |
-| `@clerk/fastify` | adapter | | Fastify plugin. |
-| `@clerk/hono` | adapter | | Hono SDK (edge / serverless). |
-| `@clerk/astro` | adapter | | Astro integration (components + server utilities). |
-| `@clerk/nuxt` | adapter | | Nuxt module (Vue). |
-| `@clerk/vue` | adapter | | Vue 3 composables and components. |
-| `@clerk/react-router` | adapter | | React Router v7 SDK. |
-| `@clerk/tanstack-react-start` | adapter | | TanStack React Start SDK. |
-| `@clerk/expo` | adapter | | React Native / Expo SDK. |
-| `@clerk/expo-native` | adapter | | Native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module for Expo, built on clerk-ios / clerk-android. Optional companion to `@clerk/expo`. |
-| `@clerk/expo-passkeys` | adapter | | Passkeys companion library for Expo. |
-| `@clerk/chrome-extension` | browser-runtime | | SDK for Chrome extension contexts. |
-| `@clerk/localizations` | ui/i18n | | Translation strings for the UI components. Consumed by `ui`. |
-| `@clerk/testing` | tooling | | E2E test helpers for consumers (Playwright + Cypress). |
-| `@clerk/msw` | tooling | | MSW request handlers for mocking the Clerk API in tests. Private (not published). |
-| `@clerk/swingset` | tooling | | Component explorer for `@clerk/mosaic`. Private (not published). |
-| `@clerk/upgrade` | tooling | | CLI codemod tool for upgrading consumers between SDK versions. |
-| `@clerk/eslint-plugin` | tooling | | ESLint plugin enforcing Clerk patterns across JavaScript frameworks (lint rules shipped to apps). Published. |
+| Package | Category | BC | Purpose |
+| ------------------------------- | --------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `@clerk/shared` | foundational | | Internal utilities used by all SDKs (storage, events, React helpers). Hosts the shared types as `@clerk/shared/types`. Most-depended-on package. |
+| `@clerk/backend` | foundational | | Backend API REST client, JWT verification, webhook helpers. Used by every server adapter. |
+| `@clerk/clerk-js` | browser-runtime | ⚠️ | The browser runtime (script tag). Backwards-compat sensitive. |
+| `@clerk/ui` | ui | ⚠️ | React components for the hosted sign-in / sign-up flows (`packages/ui/src/components`). Consumed by the react/astro/vue/chrome-extension adapters. Backwards-compat sensitive. |
+| `@clerk/mosaic` | ui | | Experimental next-generation React components. Public ESM package (`UserButton` + `styles.css`). Ships unstyled, accessible primitives (dialog, menu, popover, ...) internally under `src/primitives/`. Reads Clerk context from the host SDK via `@clerk/shared`. |
+| `@clerk/react` | adapter (core) | | React hooks and context (`useAuth`, `useUser`, `useOrganization`, ...). Shared by the React-based adapters. |
+| `@clerk/nextjs` | adapter | | Next.js SDK: middleware, route handlers, server components. |
+| `@clerk/express` | adapter | | Express middleware and server helpers. |
+| `@clerk/fastify` | adapter | | Fastify plugin. |
+| `@clerk/hono` | adapter | | Hono SDK (edge / serverless). |
+| `@clerk/astro` | adapter | | Astro integration (components + server utilities). |
+| `@clerk/nuxt` | adapter | | Nuxt module (Vue). |
+| `@clerk/vue` | adapter | | Vue 3 composables and components. |
+| `@clerk/react-router` | adapter | | React Router v7 SDK. |
+| `@clerk/tanstack-react-start` | adapter | | TanStack React Start SDK. |
+| `@clerk/expo` | adapter | | React Native / Expo SDK. |
+| `@clerk/expo-native-components` | adapter | | Native components (`AuthView`, `UserButton`, `UserProfileView`) and the native module for Expo, built on clerk-ios / clerk-android. Optional companion to `@clerk/expo`. |
+| `@clerk/expo-passkeys` | adapter | | Passkeys companion library for Expo. |
+| `@clerk/chrome-extension` | browser-runtime | | SDK for Chrome extension contexts. |
+| `@clerk/localizations` | ui/i18n | | Translation strings for the UI components. Consumed by `ui`. |
+| `@clerk/testing` | tooling | | E2E test helpers for consumers (Playwright + Cypress). |
+| `@clerk/msw` | tooling | | MSW request handlers for mocking the Clerk API in tests. Private (not published). |
+| `@clerk/swingset` | tooling | | Component explorer for `@clerk/mosaic`. Private (not published). |
+| `@clerk/upgrade` | tooling | | CLI codemod tool for upgrading consumers between SDK versions. |
+| `@clerk/eslint-plugin` | tooling | | ESLint plugin enforcing Clerk patterns across JavaScript frameworks (lint rules shipped to apps). Published. |
Tests: `pnpm turbo test --filter=@clerk/` (or `pnpm --filter @clerk/ test` after a
build). Most packages use vitest; `@clerk/backend` runs a multi-runtime suite (node + edge +
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 675d48e803f..d07fbb87c3f 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -28,9 +28,9 @@ expo:
- changed-files:
- any-glob-to-any-file: packages/expo/**
-expo-native:
+expo-native-components:
- changed-files:
- - any-glob-to-any-file: packages/expo-native/**
+ - any-glob-to-any-file: packages/expo-native-components/**
express:
- changed-files:
diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml
index 4d7ca8eda83..9feadb28f55 100644
--- a/.github/workflows/api-changes.yml
+++ b/.github/workflows/api-changes.yml
@@ -19,7 +19,7 @@ on:
- 'packages/clerk-js/**'
- 'packages/expo/**'
- 'packages/expo-google-signin/**'
- - 'packages/expo-native/**'
+ - 'packages/expo-native-components/**'
- 'packages/expo-passkeys/**'
- 'packages/express/**'
- 'packages/fastify/**'
@@ -57,7 +57,7 @@ env:
--filter=@clerk/clerk-js
--filter=@clerk/expo
--filter=@clerk/expo-google-signin
- --filter=@clerk/expo-native
+ --filter=@clerk/expo-native-components
--filter=@clerk/expo-passkeys
--filter=@clerk/express
--filter=@clerk/fastify
diff --git a/.github/workflows/expo-native-build.yml b/.github/workflows/expo-native-build.yml
index 375d3cc47d1..cd69db5c340 100644
--- a/.github/workflows/expo-native-build.yml
+++ b/.github/workflows/expo-native-build.yml
@@ -11,7 +11,7 @@ on:
- 'integration/tests/expo-native/**'
- 'packages/expo/**'
- 'packages/expo-google-signin/**'
- - 'packages/expo-native/**'
+ - 'packages/expo-native-components/**'
workflow_dispatch:
permissions:
@@ -86,7 +86,7 @@ jobs:
packages/clerk-js \
packages/expo \
packages/expo-google-signin \
- packages/expo-native \
+ packages/expo-native-components \
packages/react \
packages/shared \
"$FIXTURE_DIR" |
@@ -127,7 +127,7 @@ jobs:
mkdir -p "$SDK_PACK_DIR"
pnpm --filter @clerk/expo pack --pack-destination "$SDK_PACK_DIR"
pnpm --filter @clerk/expo-google-signin pack --pack-destination "$SDK_PACK_DIR"
- pnpm --filter @clerk/expo-native pack --pack-destination "$SDK_PACK_DIR"
+ pnpm --filter @clerk/expo-native-components pack --pack-destination "$SDK_PACK_DIR"
- name: Install fixture dependencies
if: steps.native-build-cache.outputs.cache-hit != 'true'
@@ -141,7 +141,7 @@ jobs:
# [0-9] keeps this glob off the clerk-expo-google-signin and clerk-expo-native tarballs.
SDK_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-[0-9]*.tgz)"
GOOGLE_SIGNIN_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-google-signin-*.tgz)"
- NATIVE_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-native-*.tgz)"
+ NATIVE_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-native-components-*.tgz)"
pnpm add "$SDK_TARBALL" "$GOOGLE_SIGNIN_TARBALL" "$NATIVE_TARBALL" -w
# expo-dev-client makes even release builds boot into the dev
# launcher (unreachable Metro in CI), which stalls every Maestro
diff --git a/package.json b/package.json
index 7620564b4fd..3f4c74549d6 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"changeset": "changeset",
"changeset:empty": "pnpm changeset --empty",
"clean": "turbo run clean",
- "dev": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/* --filter=!@clerk/expo --filter=!@clerk/expo-native --filter=!@clerk/tanstack-react-start --filter=!@clerk/chrome-extension",
+ "dev": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/* --filter=!@clerk/expo --filter=!@clerk/expo-native-components --filter=!@clerk/tanstack-react-start --filter=!@clerk/chrome-extension",
"dev:fe-libs": "TURBO_UI=0 FORCE_COLOR=1 turbo dev --filter=@clerk/clerk-js --filter=@clerk/ui --filter=@clerk/shared",
"dev:js": "TURBO_UI=0 FORCE_COLOR=1 turbo dev:current --filter=@clerk/clerk-js",
"dev:sandbox": "TURBO_UI=0 FORCE_COLOR=1 turbo dev:sandbox:serve",
diff --git a/packages/expo-native/.gitignore b/packages/expo-native-components/.gitignore
similarity index 100%
rename from packages/expo-native/.gitignore
rename to packages/expo-native-components/.gitignore
diff --git a/packages/expo-native/LICENSE b/packages/expo-native-components/LICENSE
similarity index 100%
rename from packages/expo-native/LICENSE
rename to packages/expo-native-components/LICENSE
diff --git a/packages/expo-native/README.md b/packages/expo-native-components/README.md
similarity index 74%
rename from packages/expo-native/README.md
rename to packages/expo-native-components/README.md
index 35fe6587c9d..dceac483ebb 100644
--- a/packages/expo-native/README.md
+++ b/packages/expo-native-components/README.md
@@ -1,33 +1,33 @@
-
+
-
@clerk/expo-native
+ @clerk/expo-native-components
[](https://clerk.com/discord)
-[](https://clerk.com/docs?utm_source=github&utm_medium=clerk_expo_native)
+[](https://clerk.com/docs?utm_source=github&utm_medium=clerk_expo_native_components)
[](https://x.com/intent/follow?screen_name=clerk)
-[Changelog](https://github.com/clerk/javascript/blob/main/packages/expo-native/CHANGELOG.md)
+[Changelog](https://github.com/clerk/javascript/blob/main/packages/expo-native-components/CHANGELOG.md)
·
[Report a Bug](https://github.com/clerk/javascript/issues/new?assignees=&labels=needs-triage&projects=&template=BUG_REPORT.yml)
·
[Request a Feature](https://feedback.clerk.com/roadmap)
·
-[Get help](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native)
+[Get help](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native_components)
## Getting Started
-`@clerk/expo-native` adds Clerk's prebuilt native components to an Expo app that uses [`@clerk/expo`](https://github.com/clerk/javascript/tree/main/packages/expo). It contains the native modules built on the Clerk iOS and Android SDKs:
+`@clerk/expo-native-components` adds Clerk's prebuilt native components to an Expo app that uses [`@clerk/expo`](https://github.com/clerk/javascript/tree/main/packages/expo). It contains the native modules built on the Clerk iOS and Android SDKs:
- **iOS**: [clerk-ios](https://github.com/clerk/clerk-ios) (SwiftUI)
- **Android**: [clerk-android](https://github.com/clerk/clerk-android) (Jetpack Compose)
@@ -43,7 +43,7 @@ Install it only if you use `AuthView`, `UserProfileView`, `UserButton`, or biome
### Installation
```sh
-npx expo install @clerk/expo @clerk/expo-native
+npx expo install @clerk/expo @clerk/expo-native-components
```
Add the config plugin alongside `@clerk/expo` in your app config, then rebuild your native app:
@@ -51,7 +51,7 @@ Add the config plugin alongside `@clerk/expo` in your app config, then rebuild y
```json
{
"expo": {
- "plugins": ["@clerk/expo", "@clerk/expo-native"]
+ "plugins": ["@clerk/expo", "@clerk/expo-native-components"]
}
}
```
@@ -65,7 +65,7 @@ The plugin accepts the following options:
```tsx
import { useAuth } from '@clerk/expo';
-import { AuthView } from '@clerk/expo-native';
+import { AuthView } from '@clerk/expo-native-components';
export default function SignInScreen() {
const { isSignedIn } = useAuth();
@@ -78,11 +78,11 @@ export default function SignInScreen() {
}
```
-For further information, guides, and examples visit the [Expo reference documentation](https://clerk.com/docs/references/expo/overview?utm_source=github&utm_medium=clerk_expo_native).
+For further information, guides, and examples visit the [Expo reference documentation](https://clerk.com/docs/references/expo/overview?utm_source=github&utm_medium=clerk_expo_native_components).
## Support
-For help, visit our [support page](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native).
+For help, visit our [support page](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_expo_native_components).
## Community
@@ -94,9 +94,9 @@ We're open to all community contributions! If you'd like to contribute in any wa
## Security
-`@clerk/expo-native` follows good practices of security, but 100% security cannot be assured.
+`@clerk/expo-native-components` follows good practices of security, but 100% security cannot be assured.
-`@clerk/expo-native` is provided **"as is"** without any **warranty**. Use at your own risk.
+`@clerk/expo-native-components` is provided **"as is"** without any **warranty**. Use at your own risk.
_For more information and to report security issues, please refer to our [security documentation](https://github.com/clerk/javascript/blob/main/docs/SECURITY.md)._
@@ -104,4 +104,4 @@ _For more information and to report security issues, please refer to our [securi
This project is licensed under the **MIT license**.
-See [LICENSE](https://github.com/clerk/javascript/blob/main/packages/expo-native/LICENSE) for more information.
+See [LICENSE](https://github.com/clerk/javascript/blob/main/packages/expo-native-components/LICENSE) for more information.
diff --git a/packages/expo-native/android/build.gradle b/packages/expo-native-components/android/build.gradle
similarity index 96%
rename from packages/expo-native/android/build.gradle
rename to packages/expo-native-components/android/build.gradle
index 18c81a17cbe..facf8a66952 100644
--- a/packages/expo-native/android/build.gradle
+++ b/packages/expo-native-components/android/build.gradle
@@ -13,7 +13,7 @@ applyKotlinExpoModulesCorePlugin()
group = 'com.clerk.expo'
version = '1.0.0'
-// Set by the @clerk/expo-native config plugin to the installed @clerk/expo version.
+// Set by the @clerk/expo-native-components config plugin to the installed @clerk/expo version.
def clerkExpoVersion = rootProject.findProperty("clerkExpo.hostSdkVersion")?.toString() ?:
new JsonSlurper().parse(new File(projectDir, "../package.json")).version.toString()
@@ -98,7 +98,7 @@ try {
}
} catch (Exception ignored) {
// Future Gradle versions with Isolated Projects may block cross-project configuration.
- // In that case, users should add '@clerk/expo-native' to their app.json plugins array instead,
+ // In that case, users should add '@clerk/expo-native-components' to their app.json plugins array instead,
// which applies the flag via the config plugin's withClerkAndroid.
}
diff --git a/packages/expo-native/android/src/main/AndroidManifest.xml b/packages/expo-native-components/android/src/main/AndroidManifest.xml
similarity index 100%
rename from packages/expo-native/android/src/main/AndroidManifest.xml
rename to packages/expo-native-components/android/src/main/AndroidManifest.xml
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt
diff --git a/packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt b/packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
similarity index 100%
rename from packages/expo-native/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
rename to packages/expo-native-components/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt
diff --git a/packages/expo-native/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt b/packages/expo-native-components/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
similarity index 100%
rename from packages/expo-native/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
rename to packages/expo-native-components/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt
diff --git a/packages/expo-native/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt b/packages/expo-native-components/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
similarity index 100%
rename from packages/expo-native/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
rename to packages/expo-native-components/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt
diff --git a/packages/expo-native-components/app.plugin.d.ts b/packages/expo-native-components/app.plugin.d.ts
new file mode 100644
index 00000000000..8808449dd73
--- /dev/null
+++ b/packages/expo-native-components/app.plugin.d.ts
@@ -0,0 +1,5 @@
+import type { ConfigPlugin } from '@expo/config-plugins';
+
+declare const withClerkExpoNativeComponents: ConfigPlugin<{ keychainService?: string; theme?: string } | void>;
+
+export = withClerkExpoNativeComponents;
diff --git a/packages/expo-native/app.plugin.js b/packages/expo-native-components/app.plugin.js
similarity index 96%
rename from packages/expo-native/app.plugin.js
rename to packages/expo-native-components/app.plugin.js
index 98a22f4b09d..36e10092b7a 100644
--- a/packages/expo-native/app.plugin.js
+++ b/packages/expo-native-components/app.plugin.js
@@ -1,5 +1,5 @@
/**
- * Expo config plugin for @clerk/expo-native
+ * Expo config plugin for @clerk/expo-native-components
* Configures iOS and Android for the Clerk native SDKs (clerk-ios / clerk-android)
*
* When this plugin is used:
@@ -306,11 +306,11 @@ const withClerkTheme = (config, props = {}) => {
return config;
};
-const withClerkExpoNative = (config, props = {}, resolve = resolveClerkExpo) => {
+const withClerkExpoNativeComponents = (config, props = {}, resolve = resolveClerkExpo) => {
const clerkExpo = resolve(config._internal?.projectRoot);
if (clerkExpo && fs.existsSync(path.join(clerkExpo.dir, 'expo-module.config.json'))) {
throw new Error(
- `Clerk: @clerk/expo@${clerkExpo.version} still bundles the Clerk native module, which conflicts with @clerk/expo-native. Upgrade @clerk/expo to a version that supports @clerk/expo-native.`,
+ `Clerk: @clerk/expo@${clerkExpo.version} still bundles the Clerk native module, which conflicts with @clerk/expo-native-components. Upgrade @clerk/expo to a version that supports @clerk/expo-native-components.`,
);
}
// Native requests report the @clerk/expo version in the x-clerk-host-sdk-version header.
@@ -323,9 +323,9 @@ const withClerkExpoNative = (config, props = {}, resolve = resolveClerkExpo) =>
return config;
};
-module.exports = createRunOncePlugin(withClerkExpoNative, packageJson.name, packageJson.version);
+module.exports = createRunOncePlugin(withClerkExpoNativeComponents, packageJson.name, packageJson.version);
module.exports._testing = {
- withClerkExpoNative,
+ withClerkExpoNativeComponents,
validateThemeJson,
isPlainObject,
VALID_COLOR_KEYS,
diff --git a/packages/expo-native/expo-module.config.json b/packages/expo-native-components/expo-module.config.json
similarity index 100%
rename from packages/expo-native/expo-module.config.json
rename to packages/expo-native-components/expo-module.config.json
diff --git a/packages/expo-native/ios/ClerkAppDelegateSubscriber.swift b/packages/expo-native-components/ios/ClerkAppDelegateSubscriber.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkAppDelegateSubscriber.swift
rename to packages/expo-native-components/ios/ClerkAppDelegateSubscriber.swift
diff --git a/packages/expo-native/ios/ClerkAuthNativeView.swift b/packages/expo-native-components/ios/ClerkAuthNativeView.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkAuthNativeView.swift
rename to packages/expo-native-components/ios/ClerkAuthNativeView.swift
diff --git a/packages/expo-native/ios/ClerkExpo.podspec b/packages/expo-native-components/ios/ClerkExpo.podspec
similarity index 100%
rename from packages/expo-native/ios/ClerkExpo.podspec
rename to packages/expo-native-components/ios/ClerkExpo.podspec
diff --git a/packages/expo-native/ios/ClerkExpoModule.swift b/packages/expo-native-components/ios/ClerkExpoModule.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkExpoModule.swift
rename to packages/expo-native-components/ios/ClerkExpoModule.swift
diff --git a/packages/expo-native/ios/ClerkNativeBridge.swift b/packages/expo-native-components/ios/ClerkNativeBridge.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkNativeBridge.swift
rename to packages/expo-native-components/ios/ClerkNativeBridge.swift
diff --git a/packages/expo-native/ios/ClerkNativeViewHost.swift b/packages/expo-native-components/ios/ClerkNativeViewHost.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkNativeViewHost.swift
rename to packages/expo-native-components/ios/ClerkNativeViewHost.swift
diff --git a/packages/expo-native/ios/ClerkUserButtonNativeView.swift b/packages/expo-native-components/ios/ClerkUserButtonNativeView.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkUserButtonNativeView.swift
rename to packages/expo-native-components/ios/ClerkUserButtonNativeView.swift
diff --git a/packages/expo-native/ios/ClerkUserProfileNativeView.swift b/packages/expo-native-components/ios/ClerkUserProfileNativeView.swift
similarity index 100%
rename from packages/expo-native/ios/ClerkUserProfileNativeView.swift
rename to packages/expo-native-components/ios/ClerkUserProfileNativeView.swift
diff --git a/packages/expo-native/ios/Tests/ClerkAuthNativeViewPaperTests.swift b/packages/expo-native-components/ios/Tests/ClerkAuthNativeViewPaperTests.swift
similarity index 100%
rename from packages/expo-native/ios/Tests/ClerkAuthNativeViewPaperTests.swift
rename to packages/expo-native-components/ios/Tests/ClerkAuthNativeViewPaperTests.swift
diff --git a/packages/expo-native/ios/Tests/ClerkNativeBridgeTests.swift b/packages/expo-native-components/ios/Tests/ClerkNativeBridgeTests.swift
similarity index 100%
rename from packages/expo-native/ios/Tests/ClerkNativeBridgeTests.swift
rename to packages/expo-native-components/ios/Tests/ClerkNativeBridgeTests.swift
diff --git a/packages/expo-native/ios/Tests/ClerkNativeViewHostTests.swift b/packages/expo-native-components/ios/Tests/ClerkNativeViewHostTests.swift
similarity index 100%
rename from packages/expo-native/ios/Tests/ClerkNativeViewHostTests.swift
rename to packages/expo-native-components/ios/Tests/ClerkNativeViewHostTests.swift
diff --git a/packages/expo-native/ios/Tests/ClerkUserProfileCustomPageStateTests.swift b/packages/expo-native-components/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
similarity index 100%
rename from packages/expo-native/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
rename to packages/expo-native-components/ios/Tests/ClerkUserProfileCustomPageStateTests.swift
diff --git a/packages/expo-native/package.json b/packages/expo-native-components/package.json
similarity index 95%
rename from packages/expo-native/package.json
rename to packages/expo-native-components/package.json
index f732b7297da..0d6f430f012 100644
--- a/packages/expo-native/package.json
+++ b/packages/expo-native-components/package.json
@@ -1,5 +1,5 @@
{
- "name": "@clerk/expo-native",
+ "name": "@clerk/expo-native-components",
"version": "0.0.1",
"description": "Native Clerk components and native SDK integration for Expo, powered by clerk-ios and clerk-android",
"keywords": [
@@ -18,7 +18,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/clerk/javascript.git",
- "directory": "packages/expo-native"
+ "directory": "packages/expo-native-components"
},
"license": "MIT",
"author": "Clerk",
diff --git a/packages/expo-native/react-native.config.js b/packages/expo-native-components/react-native.config.js
similarity index 100%
rename from packages/expo-native/react-native.config.js
rename to packages/expo-native-components/react-native.config.js
diff --git a/packages/expo-native/src/AuthView.tsx b/packages/expo-native-components/src/AuthView.tsx
similarity index 92%
rename from packages/expo-native/src/AuthView.tsx
rename to packages/expo-native-components/src/AuthView.tsx
index 7734de4dd86..99e1d395348 100644
--- a/packages/expo-native/src/AuthView.tsx
+++ b/packages/expo-native-components/src/AuthView.tsx
@@ -24,7 +24,7 @@ type AuthNativeEvent = NativeSyntheticEvent>;
*
* @example
* ```tsx
- * import { AuthView } from '@clerk/expo-native';
+ * import { AuthView } from '@clerk/expo-native-components';
* import { useAuth } from '@clerk/expo';
*
* export default function SignInScreen() {
@@ -63,7 +63,7 @@ export function AuthView({
{!isNativeSupported
? 'Native AuthView is only available on iOS and Android'
- : 'Native AuthView requires a development build with the @clerk/expo-native config plugin. Add "@clerk/expo-native" to your app.json plugins array and rebuild your native app.'}
+ : 'Native AuthView requires a development build with the @clerk/expo-native-components config plugin. Add "@clerk/expo-native-components" to your app.json plugins array and rebuild your native app.'}
);
diff --git a/packages/expo-native/src/AuthView.types.ts b/packages/expo-native-components/src/AuthView.types.ts
similarity index 100%
rename from packages/expo-native/src/AuthView.types.ts
rename to packages/expo-native-components/src/AuthView.types.ts
diff --git a/packages/expo-native/src/EmbeddedNavigation.types.ts b/packages/expo-native-components/src/EmbeddedNavigation.types.ts
similarity index 100%
rename from packages/expo-native/src/EmbeddedNavigation.types.ts
rename to packages/expo-native-components/src/EmbeddedNavigation.types.ts
diff --git a/packages/expo-native/src/UserButton.tsx b/packages/expo-native-components/src/UserButton.tsx
similarity index 98%
rename from packages/expo-native/src/UserButton.tsx
rename to packages/expo-native-components/src/UserButton.tsx
index f3038fd4efc..fc2504781b2 100644
--- a/packages/expo-native/src/UserButton.tsx
+++ b/packages/expo-native-components/src/UserButton.tsx
@@ -47,7 +47,7 @@ export interface UserButtonProps {
*
* @example
* ```tsx
- * import { UserButton } from '@clerk/expo-native';
+ * import { UserButton } from '@clerk/expo-native-components';
*
* export default function Home() {
* return (
diff --git a/packages/expo-native/src/UserProfileCustomPages.tsx b/packages/expo-native-components/src/UserProfileCustomPages.tsx
similarity index 100%
rename from packages/expo-native/src/UserProfileCustomPages.tsx
rename to packages/expo-native-components/src/UserProfileCustomPages.tsx
diff --git a/packages/expo-native/src/UserProfileView.tsx b/packages/expo-native-components/src/UserProfileView.tsx
similarity index 96%
rename from packages/expo-native/src/UserProfileView.tsx
rename to packages/expo-native-components/src/UserProfileView.tsx
index 32aa4b5a6a9..eb3e59bee96 100644
--- a/packages/expo-native/src/UserProfileView.tsx
+++ b/packages/expo-native-components/src/UserProfileView.tsx
@@ -75,7 +75,7 @@ export interface UserProfileViewProps extends EmbeddedNavigationProps {
*
* @example
* ```tsx
- * import { UserProfileView } from '@clerk/expo-native';
+ * import { UserProfileView } from '@clerk/expo-native-components';
* import { useAuth } from '@clerk/expo';
*
* export default function ProfileScreen() {
@@ -128,7 +128,7 @@ export function UserProfileView({
{!isNativeSupported
? 'Native UserProfileView is only available on iOS and Android'
- : 'Native UserProfileView requires a development build with the @clerk/expo-native config plugin. Add "@clerk/expo-native" to your app.json plugins array and rebuild your native app.'}
+ : 'Native UserProfileView requires a development build with the @clerk/expo-native-components config plugin. Add "@clerk/expo-native-components" to your app.json plugins array and rebuild your native app.'}
);
diff --git a/packages/expo-native/src/__tests__/AuthView.test.tsx b/packages/expo-native-components/src/__tests__/AuthView.test.tsx
similarity index 100%
rename from packages/expo-native/src/__tests__/AuthView.test.tsx
rename to packages/expo-native-components/src/__tests__/AuthView.test.tsx
diff --git a/packages/expo-native/src/__tests__/UserButton.test.tsx b/packages/expo-native-components/src/__tests__/UserButton.test.tsx
similarity index 100%
rename from packages/expo-native/src/__tests__/UserButton.test.tsx
rename to packages/expo-native-components/src/__tests__/UserButton.test.tsx
diff --git a/packages/expo-native/src/__tests__/UserProfileCustomPages.test.tsx b/packages/expo-native-components/src/__tests__/UserProfileCustomPages.test.tsx
similarity index 100%
rename from packages/expo-native/src/__tests__/UserProfileCustomPages.test.tsx
rename to packages/expo-native-components/src/__tests__/UserProfileCustomPages.test.tsx
diff --git a/packages/expo-native/src/__tests__/UserProfileView.test.tsx b/packages/expo-native-components/src/__tests__/UserProfileView.test.tsx
similarity index 100%
rename from packages/expo-native/src/__tests__/UserProfileView.test.tsx
rename to packages/expo-native-components/src/__tests__/UserProfileView.test.tsx
diff --git a/packages/expo-native/src/__tests__/appPlugin.test.js b/packages/expo-native-components/src/__tests__/appPlugin.test.js
similarity index 80%
rename from packages/expo-native/src/__tests__/appPlugin.test.js
rename to packages/expo-native-components/src/__tests__/appPlugin.test.js
index bb611cb434b..bbb2d247250 100644
--- a/packages/expo-native/src/__tests__/appPlugin.test.js
+++ b/packages/expo-native-components/src/__tests__/appPlugin.test.js
@@ -5,12 +5,12 @@ import path from 'node:path';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
// eslint-disable-next-line @typescript-eslint/no-require-imports -- CJS plugin, no ESM export
-const { withClerkExpoNative } = require('../../app.plugin.js')._testing;
+const { withClerkExpoNativeComponents } = require('../../app.plugin.js')._testing;
const applyMod = (config, platform, mod, modResults) =>
config.mods[platform][mod]({ ...config, modRequest: {}, modResults });
-describe('withClerkExpoNative', () => {
+describe('withClerkExpoNativeComponents', () => {
let clerkExpoDir;
beforeEach(() => {
@@ -24,7 +24,7 @@ describe('withClerkExpoNative', () => {
});
test('reports the installed @clerk/expo version on iOS and Android', async () => {
- const config = withClerkExpoNative({ name: 'test', slug: 'test' }, {}, () => ({
+ const config = withClerkExpoNativeComponents({ name: 'test', slug: 'test' }, {}, () => ({
dir: clerkExpoDir,
version: '4.8.0',
}));
@@ -44,7 +44,10 @@ describe('withClerkExpoNative', () => {
fs.writeFileSync(path.join(clerkExpoDir, 'expo-module.config.json'), '{}');
expect(() =>
- withClerkExpoNative({ name: 'test', slug: 'test' }, {}, () => ({ dir: clerkExpoDir, version: '4.7.1' })),
+ withClerkExpoNativeComponents({ name: 'test', slug: 'test' }, {}, () => ({
+ dir: clerkExpoDir,
+ version: '4.7.1',
+ })),
).toThrow('@clerk/expo@4.7.1 still bundles the Clerk native module');
});
});
diff --git a/packages/expo-native/src/__tests__/appPlugin.theme.test.js b/packages/expo-native-components/src/__tests__/appPlugin.theme.test.js
similarity index 100%
rename from packages/expo-native/src/__tests__/appPlugin.theme.test.js
rename to packages/expo-native-components/src/__tests__/appPlugin.theme.test.js
diff --git a/packages/expo-native/src/__tests__/useAuthViewState.test.tsx b/packages/expo-native-components/src/__tests__/useAuthViewState.test.tsx
similarity index 100%
rename from packages/expo-native/src/__tests__/useAuthViewState.test.tsx
rename to packages/expo-native-components/src/__tests__/useAuthViewState.test.tsx
diff --git a/packages/expo-native/src/index.ts b/packages/expo-native-components/src/index.ts
similarity index 87%
rename from packages/expo-native/src/index.ts
rename to packages/expo-native-components/src/index.ts
index a20538f6186..c102a43da33 100644
--- a/packages/expo-native/src/index.ts
+++ b/packages/expo-native-components/src/index.ts
@@ -7,12 +7,12 @@
*
* ## Installation
*
- * Native components require the `@clerk/expo-native` config plugin in your `app.json`, alongside `@clerk/expo`:
+ * Native components require the `@clerk/expo-native-components` config plugin in your `app.json`, alongside `@clerk/expo`:
*
* ```json
* {
* "expo": {
- * "plugins": ["@clerk/expo", "@clerk/expo-native"]
+ * "plugins": ["@clerk/expo", "@clerk/expo-native-components"]
* }
* }
* ```
@@ -25,7 +25,7 @@
* - {@link UserProfileView} - User profile and account management, renders inline
* - {@link UserButton} - Avatar button that opens the native user profile
*
- * @module @clerk/expo-native
+ * @module @clerk/expo-native-components
*/
export { AuthView } from './AuthView';
diff --git a/packages/expo-native/src/specs/NativeClerkAuthView.android.ts b/packages/expo-native-components/src/specs/NativeClerkAuthView.android.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkAuthView.android.ts
rename to packages/expo-native-components/src/specs/NativeClerkAuthView.android.ts
diff --git a/packages/expo-native/src/specs/NativeClerkAuthView.ts b/packages/expo-native-components/src/specs/NativeClerkAuthView.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkAuthView.ts
rename to packages/expo-native-components/src/specs/NativeClerkAuthView.ts
diff --git a/packages/expo-native/src/specs/NativeClerkModule.ts b/packages/expo-native-components/src/specs/NativeClerkModule.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkModule.ts
rename to packages/expo-native-components/src/specs/NativeClerkModule.ts
diff --git a/packages/expo-native/src/specs/NativeClerkModule.types.ts b/packages/expo-native-components/src/specs/NativeClerkModule.types.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkModule.types.ts
rename to packages/expo-native-components/src/specs/NativeClerkModule.types.ts
diff --git a/packages/expo-native/src/specs/NativeClerkModule.web.ts b/packages/expo-native-components/src/specs/NativeClerkModule.web.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkModule.web.ts
rename to packages/expo-native-components/src/specs/NativeClerkModule.web.ts
diff --git a/packages/expo-native/src/specs/NativeClerkUserButtonView.android.ts b/packages/expo-native-components/src/specs/NativeClerkUserButtonView.android.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkUserButtonView.android.ts
rename to packages/expo-native-components/src/specs/NativeClerkUserButtonView.android.ts
diff --git a/packages/expo-native/src/specs/NativeClerkUserButtonView.ts b/packages/expo-native-components/src/specs/NativeClerkUserButtonView.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkUserButtonView.ts
rename to packages/expo-native-components/src/specs/NativeClerkUserButtonView.ts
diff --git a/packages/expo-native/src/specs/NativeClerkUserProfileView.android.ts b/packages/expo-native-components/src/specs/NativeClerkUserProfileView.android.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkUserProfileView.android.ts
rename to packages/expo-native-components/src/specs/NativeClerkUserProfileView.android.ts
diff --git a/packages/expo-native/src/specs/NativeClerkUserProfileView.ts b/packages/expo-native-components/src/specs/NativeClerkUserProfileView.ts
similarity index 100%
rename from packages/expo-native/src/specs/NativeClerkUserProfileView.ts
rename to packages/expo-native-components/src/specs/NativeClerkUserProfileView.ts
diff --git a/packages/expo-native/src/specs/__tests__/native-view.web.test.ts b/packages/expo-native-components/src/specs/__tests__/native-view.web.test.ts
similarity index 100%
rename from packages/expo-native/src/specs/__tests__/native-view.web.test.ts
rename to packages/expo-native-components/src/specs/__tests__/native-view.web.test.ts
diff --git a/packages/expo-native/src/useAuthViewState.ts b/packages/expo-native-components/src/useAuthViewState.ts
similarity index 100%
rename from packages/expo-native/src/useAuthViewState.ts
rename to packages/expo-native-components/src/useAuthViewState.ts
diff --git a/packages/expo-native/src/utils/native-module.ts b/packages/expo-native-components/src/utils/native-module.ts
similarity index 100%
rename from packages/expo-native/src/utils/native-module.ts
rename to packages/expo-native-components/src/utils/native-module.ts
diff --git a/packages/expo-native/tsconfig.declarations.json b/packages/expo-native-components/tsconfig.declarations.json
similarity index 100%
rename from packages/expo-native/tsconfig.declarations.json
rename to packages/expo-native-components/tsconfig.declarations.json
diff --git a/packages/expo-native/tsconfig.json b/packages/expo-native-components/tsconfig.json
similarity index 100%
rename from packages/expo-native/tsconfig.json
rename to packages/expo-native-components/tsconfig.json
diff --git a/packages/expo-native/tsconfig.test.json b/packages/expo-native-components/tsconfig.test.json
similarity index 100%
rename from packages/expo-native/tsconfig.test.json
rename to packages/expo-native-components/tsconfig.test.json
diff --git a/packages/expo-native/tsdown.config.mts b/packages/expo-native-components/tsdown.config.mts
similarity index 100%
rename from packages/expo-native/tsdown.config.mts
rename to packages/expo-native-components/tsdown.config.mts
diff --git a/packages/expo-native/vitest.config.mts b/packages/expo-native-components/vitest.config.mts
similarity index 100%
rename from packages/expo-native/vitest.config.mts
rename to packages/expo-native-components/vitest.config.mts
diff --git a/packages/expo-native/vitest.setup.mts b/packages/expo-native-components/vitest.setup.mts
similarity index 100%
rename from packages/expo-native/vitest.setup.mts
rename to packages/expo-native-components/vitest.setup.mts
diff --git a/packages/expo-native/app.plugin.d.ts b/packages/expo-native/app.plugin.d.ts
deleted file mode 100644
index 76cfe1c1acd..00000000000
--- a/packages/expo-native/app.plugin.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { ConfigPlugin } from '@expo/config-plugins';
-
-declare const withClerkExpoNative: ConfigPlugin<{ keychainService?: string; theme?: string } | void>;
-
-export = withClerkExpoNative;
diff --git a/packages/expo/app.plugin.js b/packages/expo/app.plugin.js
index 29c276c2067..ecba866b513 100644
--- a/packages/expo/app.plugin.js
+++ b/packages/expo/app.plugin.js
@@ -4,11 +4,11 @@
* When this plugin is used:
* 1. Android registers the hosted auth callback intent filter
* 2. iOS gets the Sign in with Apple entitlement and, when configured, the Face ID usage description
- * 3. If @clerk/expo-native is installed, its config plugin is applied for the Clerk native SDKs
+ * 3. If @clerk/expo-native-components is installed, its config plugin is applied for the Clerk native SDKs
*/
const { AndroidConfig, withAndroidManifest, withEntitlementsPlist, withInfoPlist } = require('@expo/config-plugins');
-const CLERK_EXPO_NATIVE = '@clerk/expo-native';
+const CLERK_EXPO_NATIVE = '@clerk/expo-native-components';
const CLERK_EXPO_NATIVE_OPTIONS = ['keychainService', 'theme'];
const addHostedAuthIntentFilter = (mainActivity, packageName) => {
@@ -104,10 +104,10 @@ const getListedPluginProps = (config, name) => {
};
/**
- * Apply the @clerk/expo-native config plugin when it is installed, so apps that only list
+ * Apply the @clerk/expo-native-components config plugin when it is installed, so apps that only list
* "@clerk/expo" keep the iOS deployment target and native SDK configuration they need.
*/
-const withClerkExpoNative = (config, props = {}, resolvePlugin = resolveClerkExpoNativePlugin) => {
+const withClerkExpoNativeComponents = (config, props = {}, resolvePlugin = resolveClerkExpoNativePlugin) => {
const nativeProps = Object.fromEntries(
CLERK_EXPO_NATIVE_OPTIONS.filter(option => props[option] !== undefined).map(option => [option, props[option]]),
);
@@ -144,13 +144,13 @@ const withClerkExpo = (config, props = {}) => {
}
config = withClerkHostedAuthCallback(config);
config = withClerkFaceIDPermission(config, props);
- config = withClerkExpoNative(config, props);
+ config = withClerkExpoNativeComponents(config, props);
return config;
};
module.exports = withClerkExpo;
module.exports._testing = {
addHostedAuthIntentFilter,
- withClerkExpoNative,
+ withClerkExpoNativeComponents,
withClerkFaceIDPermission,
};
diff --git a/packages/expo/native/index.d.ts b/packages/expo/native/index.d.ts
index 0c04017d415..9d8fb781ea9 100644
--- a/packages/expo/native/index.d.ts
+++ b/packages/expo/native/index.d.ts
@@ -1 +1 @@
-export * from '@clerk/expo-native';
+export * from '@clerk/expo-native-components';
diff --git a/packages/expo/package.json b/packages/expo/package.json
index 46deb108a47..095cc4ddba5 100644
--- a/packages/expo/package.json
+++ b/packages/expo/package.json
@@ -122,7 +122,7 @@
},
"devDependencies": {
"@clerk/expo-google-signin": "workspace:*",
- "@clerk/expo-native": "workspace:*",
+ "@clerk/expo-native-components": "workspace:*",
"@clerk/expo-passkeys": "workspace:*",
"@expo/config-plugins": "^54.0.4",
"@types/base-64": "^1.0.2",
@@ -138,7 +138,7 @@
},
"peerDependencies": {
"@clerk/expo-google-signin": ">=0.1.0",
- "@clerk/expo-native": ">=0.1.0",
+ "@clerk/expo-native-components": ">=0.1.0",
"@clerk/expo-passkeys": ">=0.0.6",
"expo": "catalog:peer-expo",
"expo-apple-authentication": ">=7.0.0",
@@ -156,7 +156,7 @@
"@clerk/expo-google-signin": {
"optional": true
},
- "@clerk/expo-native": {
+ "@clerk/expo-native-components": {
"optional": true
},
"@clerk/expo-passkeys": {
diff --git a/packages/expo/src/__tests__/appPlugin.test.js b/packages/expo/src/__tests__/appPlugin.test.js
index 78e46629533..55f9e5abdc3 100644
--- a/packages/expo/src/__tests__/appPlugin.test.js
+++ b/packages/expo/src/__tests__/appPlugin.test.js
@@ -1,7 +1,7 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
// eslint-disable-next-line @typescript-eslint/no-require-imports -- CJS plugin, no ESM export
-const { withClerkExpoNative, withClerkFaceIDPermission } = require('../../app.plugin.js')._testing;
+const { withClerkExpoNativeComponents, withClerkFaceIDPermission } = require('../../app.plugin.js')._testing;
function applyInfoPlistMod(config, modResults) {
return config.mods.ios.infoPlist({
@@ -50,16 +50,16 @@ describe('withClerkFaceIDPermission', () => {
});
});
-describe('withClerkExpoNative', () => {
+describe('withClerkExpoNativeComponents', () => {
afterEach(() => {
vi.restoreAllMocks();
});
- test('applies the @clerk/expo-native plugin with its options when it is installed', () => {
+ test('applies the @clerk/expo-native-components plugin with its options when it is installed', () => {
const nativePlugin = vi.fn(config => ({ ...config, applied: true }));
const config = { name: 'test', slug: 'test' };
- const result = withClerkExpoNative(
+ const result = withClerkExpoNativeComponents(
config,
{ keychainService: 'com.example.shared', theme: './theme.json', appleSignIn: false },
() => nativePlugin,
@@ -69,15 +69,19 @@ describe('withClerkExpoNative', () => {
expect(nativePlugin).toHaveBeenCalledWith(config, { keychainService: 'com.example.shared', theme: './theme.json' });
});
- test('prefers options passed to an explicitly listed @clerk/expo-native plugin', () => {
+ test('prefers options passed to an explicitly listed @clerk/expo-native-components plugin', () => {
const nativePlugin = vi.fn(config => config);
const config = {
name: 'test',
slug: 'test',
- plugins: ['@clerk/expo', ['@clerk/expo-native', { theme: './native-theme.json' }]],
+ plugins: ['@clerk/expo', ['@clerk/expo-native-components', { theme: './native-theme.json' }]],
};
- withClerkExpoNative(config, { keychainService: 'com.example.shared', theme: './theme.json' }, () => nativePlugin);
+ withClerkExpoNativeComponents(
+ config,
+ { keychainService: 'com.example.shared', theme: './theme.json' },
+ () => nativePlugin,
+ );
expect(nativePlugin).toHaveBeenCalledWith(config, {
keychainService: 'com.example.shared',
@@ -85,36 +89,40 @@ describe('withClerkExpoNative', () => {
});
});
- test('does not apply the @clerk/expo-native plugin twice', () => {
+ test('does not apply the @clerk/expo-native-components plugin twice', () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
const nativePlugin = vi.fn(config => config);
const config = {
name: 'test',
slug: 'test',
- _internal: { pluginHistory: { '@clerk/expo-native': { name: '@clerk/expo-native', version: '0.0.1' } } },
+ _internal: {
+ pluginHistory: { '@clerk/expo-native-components': { name: '@clerk/expo-native-components', version: '0.0.1' } },
+ },
};
- expect(withClerkExpoNative(config, {}, () => nativePlugin)).toBe(config);
+ expect(withClerkExpoNativeComponents(config, {}, () => nativePlugin)).toBe(config);
expect(nativePlugin).not.toHaveBeenCalled();
expect(warn).not.toHaveBeenCalled();
- withClerkExpoNative(config, { theme: './theme.json' }, () => nativePlugin);
- expect(warn).toHaveBeenCalledWith(expect.stringContaining('Pass them to the "@clerk/expo-native" plugin instead'));
+ withClerkExpoNativeComponents(config, { theme: './theme.json' }, () => nativePlugin);
+ expect(warn).toHaveBeenCalledWith(
+ expect.stringContaining('Pass them to the "@clerk/expo-native-components" plugin instead'),
+ );
});
- test('leaves the config untouched when @clerk/expo-native is not installed', () => {
+ test('leaves the config untouched when @clerk/expo-native-components is not installed', () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
const config = { name: 'test', slug: 'test' };
- expect(withClerkExpoNative(config, {}, () => null)).toBe(config);
+ expect(withClerkExpoNativeComponents(config, {}, () => null)).toBe(config);
expect(warn).not.toHaveBeenCalled();
});
- test('warns with install instructions when native-only options are passed without @clerk/expo-native', () => {
+ test('warns with install instructions when native-only options are passed without @clerk/expo-native-components', () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
const config = { name: 'test', slug: 'test' };
- expect(withClerkExpoNative(config, { keychainService: 'com.example.shared' }, () => null)).toBe(config);
- expect(warn).toHaveBeenCalledWith(expect.stringContaining('npx expo install @clerk/expo-native'));
+ expect(withClerkExpoNativeComponents(config, { keychainService: 'com.example.shared' }, () => null)).toBe(config);
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('npx expo install @clerk/expo-native-components'));
});
});
diff --git a/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts b/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
index 1b7a7c46c5d..0c15d195c86 100644
--- a/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
+++ b/packages/expo/src/biometric-credentials/__tests__/useBiometricCredentials.test.ts
@@ -542,12 +542,12 @@ describe('useBiometricCredentials on iOS', () => {
});
});
- test('explains how to install @clerk/expo-native when the native module is missing', async () => {
+ test('explains how to install @clerk/expo-native-components when the native module is missing', async () => {
mocks.isNativeModuleInstalled = false;
try {
await expect(renderBiometricCredentials().signIn()).rejects.toThrow(
- 'Biometric credentials require the @clerk/expo-native package in a development build. Install it with `npx expo install @clerk/expo-native`',
+ 'Biometric credentials require the @clerk/expo-native-components package in a development build. Install it with `npx expo install @clerk/expo-native-components`',
);
} finally {
mocks.isNativeModuleInstalled = true;
@@ -560,7 +560,7 @@ describe('useBiometricCredentials on iOS', () => {
try {
await expect(renderBiometricCredentials().signIn()).rejects.toThrow(
- 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native.',
+ 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native-components.',
);
} finally {
Object.assign(mocks.nativeModule, { signInWithTrustedDevice });
diff --git a/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts b/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
index 49f611363f1..65af8878503 100644
--- a/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
+++ b/packages/expo/src/biometric-credentials/useBiometricCredentials.shared.ts
@@ -15,7 +15,7 @@ import type {
const DEFAULT_POLICY = 'biometry_current_set';
const CLERK_EXPO_NATIVE_INSTALL_INSTRUCTIONS =
- 'Install it with `npx expo install @clerk/expo-native`, add "@clerk/expo-native" to the plugins array in your app config, then rebuild your development build.';
+ 'Install it with `npx expo install @clerk/expo-native-components`, add "@clerk/expo-native-components" to the plugins array in your app config, then rebuild your development build.';
function toBiometricCredentialPlatform(platform: string): BiometricCredentialPlatform {
return platform === 'ios' || platform === 'android' ? platform : 'unknown';
@@ -30,7 +30,7 @@ function getNativeModule(): NativeBiometricCredentialModule {
if (!nativeModule) {
return errorThrower.throw(
- `Biometric credentials require the @clerk/expo-native package in a development build. ${CLERK_EXPO_NATIVE_INSTALL_INSTRUCTIONS}`,
+ `Biometric credentials require the @clerk/expo-native-components package in a development build. ${CLERK_EXPO_NATIVE_INSTALL_INSTRUCTIONS}`,
);
}
@@ -42,7 +42,7 @@ function getNativeModule(): NativeBiometricCredentialModule {
!nativeModule.signInWithTrustedDevice
) {
return errorThrower.throw(
- 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native.',
+ 'Biometric credentials require a development build containing a compatible version of @clerk/expo-native-components.',
);
}
@@ -95,7 +95,7 @@ function createBiometricCredentials(clerk: ReturnType): UseBiom
const nativeModule = getNativeModule();
if (typeof nativeModule.reverifyWithBiometrics !== 'function') {
return errorThrower.throw(
- 'Biometric reverification requires a development build containing a compatible version of @clerk/expo-native.',
+ 'Biometric reverification requires a development build containing a compatible version of @clerk/expo-native-components.',
);
}
const level = params?.level ?? 'first_factor';
diff --git a/packages/expo/src/native/__tests__/index.test.ts b/packages/expo/src/native/__tests__/index.test.ts
index 6d936310043..f7904bb2a8b 100644
--- a/packages/expo/src/native/__tests__/index.test.ts
+++ b/packages/expo/src/native/__tests__/index.test.ts
@@ -9,7 +9,7 @@ describe('@clerk/expo/native', () => {
vi.restoreAllMocks();
});
- describe('when @clerk/expo-native is not installed', () => {
+ describe('when @clerk/expo-native-components is not installed', () => {
beforeEach(() => {
vi.doMock('../loadClerkExpoNative', () => ({ loadClerkExpoNative: () => null }));
});
@@ -20,7 +20,7 @@ describe('@clerk/expo/native', () => {
const Component = (await importNativeEntry())[name] as (props: object) => unknown;
expect(() => Component({})).toThrow(
- /`.+` is unavailable\. Native components have moved to the @clerk\/expo-native package\. Install it with `npx expo install @clerk\/expo-native`/,
+ /`.+` is unavailable\. Native components have moved to the @clerk\/expo-native-components package\. Install it with `npx expo install @clerk\/expo-native-components`/,
);
},
);
@@ -31,12 +31,12 @@ describe('@clerk/expo/native', () => {
const hook = (await importNativeEntry())[name] as () => unknown;
expect(() => hook()).toThrow(`\`${name}\` is unavailable.`);
- expect(() => hook()).toThrow('add "@clerk/expo-native" to the plugins array in your app config');
+ expect(() => hook()).toThrow('add "@clerk/expo-native-components" to the plugins array in your app config');
},
);
});
- test('re-exports @clerk/expo-native when it is installed', async () => {
+ test('re-exports @clerk/expo-native-components when it is installed', async () => {
const clerkExpoNative = {
AuthView: vi.fn(() => null),
UserButton: vi.fn(() => null),
diff --git a/packages/expo/src/native/index.ts b/packages/expo/src/native/index.ts
index 34cc5902993..67acaa9ab68 100644
--- a/packages/expo/src/native/index.ts
+++ b/packages/expo/src/native/index.ts
@@ -2,12 +2,12 @@ import { errorThrower } from '../errorThrower';
import type { ClerkExpoNativeModule } from './loadClerkExpoNative';
import { loadClerkExpoNative } from './loadClerkExpoNative';
-// Public types are declared in native/index.d.ts, which re-exports them from @clerk/expo-native.
+// Public types are declared in native/index.d.ts, which re-exports them from @clerk/expo-native-components.
const CLERK_EXPO_NATIVE_MISSING_MESSAGE =
- 'Native components have moved to the @clerk/expo-native package. ' +
- 'Install it with `npx expo install @clerk/expo-native`, add "@clerk/expo-native" to the plugins array in your app config, ' +
- 'then rebuild your native app. You can then import them from "@clerk/expo-native".';
+ 'Native components have moved to the @clerk/expo-native-components package. ' +
+ 'Install it with `npx expo install @clerk/expo-native-components`, add "@clerk/expo-native-components" to the plugins array in your app config, ' +
+ 'then rebuild your native app. You can then import them from "@clerk/expo-native-components".';
const clerkExpoNative = loadClerkExpoNative();
diff --git a/packages/expo/src/native/loadClerkExpoNative.ts b/packages/expo/src/native/loadClerkExpoNative.ts
index 06f8e66b9dc..194b0bed1d9 100644
--- a/packages/expo/src/native/loadClerkExpoNative.ts
+++ b/packages/expo/src/native/loadClerkExpoNative.ts
@@ -9,9 +9,9 @@ export type ClerkExpoNativeModule = Partial<
export function loadClerkExpoNative(): ClerkExpoNativeModule | null {
try {
- // Synchronous require() in try/catch so Metro treats @clerk/expo-native as an optional dependency.
+ // Synchronous require() in try/catch so Metro treats @clerk/expo-native-components as an optional dependency.
// eslint-disable-next-line @typescript-eslint/no-require-imports
- return require('@clerk/expo-native') as ClerkExpoNativeModule;
+ return require('@clerk/expo-native-components') as ClerkExpoNativeModule;
} catch {
return null;
}
diff --git a/packages/expo/src/provider/nativeClientSync.tsx b/packages/expo/src/provider/nativeClientSync.tsx
index 99adbcd6e38..5cdb87dc6ae 100644
--- a/packages/expo/src/provider/nativeClientSync.tsx
+++ b/packages/expo/src/provider/nativeClientSync.tsx
@@ -1163,7 +1163,7 @@ export function useNativeClientBootstrap({
if (__DEV__) {
console.debug(
`[ClerkProvider] Native Clerk module not available. ` +
- `To enable native features, install @clerk/expo-native and add "@clerk/expo-native" to your app.json plugins array.`,
+ `To enable native features, install @clerk/expo-native-components and add "@clerk/expo-native-components" to your app.json plugins array.`,
);
}
} else if (__DEV__) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0b78f04fdd7..d37b3a454fa 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -662,9 +662,9 @@ importers:
'@clerk/expo-google-signin':
specifier: workspace:*
version: link:../expo-google-signin
- '@clerk/expo-native':
+ '@clerk/expo-native-components':
specifier: workspace:*
- version: link:../expo-native
+ version: link:../expo-native-components
'@clerk/expo-passkeys':
specifier: workspace:*
version: link:../expo-passkeys
@@ -711,7 +711,7 @@ importers:
specifier: ~54.0.36
version: 54.0.36(@babel/core@7.29.7)(bufferutil@4.1.0)(graphql@16.14.1)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@12.3.7(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@18.3.28)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)
- packages/expo-native:
+ packages/expo-native-components:
dependencies:
'@clerk/react':
specifier: workspace:^