Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6da3129
feat(expo): block app versions older than the minimum set in the Cler…
seanperez29 Oct 21, 2026
bc4b7c8
fix(expo): reload when a blocked app returns to the foreground
seanperez29 Oct 21, 2026
772f327
chore(expo): dedupe the lockfile
seanperez29 Oct 21, 2026
1eeded4
fix(expo): restart Clerk when the app version block lifts after a fai…
seanperez29 Oct 21, 2026
9ac3308
fix(expo): keep the default API version, since minimum versions apply…
seanperez29 Oct 22, 2026
2da5e0c
fix(expo): let only a rejection outlive an environment fetch
seanperez29 Oct 22, 2026
1fc8c09
fix(expo): keep the environment's minimum when a response marks the a…
seanperez29 Oct 22, 2026
0b7e9ac
refactor(expo): trim leftovers in the app version support code
seanperez29 Oct 22, 2026
1c199e8
fix(expo): let the server's latest answer outrank the environment's m…
seanperez29 Oct 22, 2026
43eb2fd
fix(expo): drop the account-security reason from the update screen
seanperez29 Oct 23, 2026
c8c932d
fix(expo): turn off the native SDKs' update screens
seanperez29 Oct 23, 2026
0ea9bb0
refactor(expo): name the app version request hooks' return type
seanperez29 Oct 23, 2026
61c5660
fix(expo): notify app version subscribers after a reset, not during it
seanperez29 Oct 23, 2026
7c125e2
fix(expo): keep the native update screen unless @clerk/expo replaces it
seanperez29 Oct 23, 2026
c1be59f
refactor(expo): read the native update screen setting when native con…
seanperez29 Oct 23, 2026
9b256bd
fix(expo): keep UpdateRequiredView internal
seanperez29 Oct 23, 2026
966e71b
fix(expo): ignore app version answers to requests sent before the las…
seanperez29 Oct 26, 2026
5311c1d
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 26, 2026
418dee2
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 26, 2026
f4e989d
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 26, 2026
1ff3538
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 26, 2026
1a447b0
fix(expo): fill a rejection's missing fields from the latest environment
seanperez29 Oct 26, 2026
ffbc117
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 26, 2026
2733ed5
chore(expo): pin clerk-ios to the force update PR's latest commit
seanperez29 Oct 27, 2026
3738e93
docs(expo): say native components still block apps without expo-appli…
seanperez29 Oct 27, 2026
8b300fd
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
08d25af
fix(expo): recover a blocked cold start without recreating Clerk
seanperez29 Oct 27, 2026
74cd552
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
ac289fd
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
38d41b8
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
2d823a5
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
1d05d27
feat(expo): check the app version when the app returns to the foreground
seanperez29 Oct 27, 2026
66177df
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
8ebd015
fix(expo): opt the foreground version check in to an empty response
seanperez29 Oct 27, 2026
6640a02
fix(expo): skip the foreground version check while one is still pending
seanperez29 Oct 27, 2026
f02b5a6
Merge branch 'sean/expo-headless-load-recovery' into sean/native-app-…
seanperez29 Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/expo-native-app-minimum-versions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@clerk/expo': minor
---

Support the minimum app versions set in the Clerk Dashboard. When your iOS or Android app's version is older than its minimum, Clerk rejects the app's requests until the user updates, and `<ClerkProvider>` shows an update screen over your app with a link to the store and, when your instance has a support email, a way to contact support.

To show your own screen instead, set `updateRequiredViewEnabled={false}` on `<ClerkProvider>` and read the new `useAppVersionSupport()` hook, which returns `isSupported`, `currentVersion`, `minimumVersion`, and `updateUrl`.

Install `expo-application` (`npx expo install expo-application`), which Clerk uses to read your app's bundle ID or package name and its version. Without it, `useAppVersionSupport()` always reports the app as supported, Clerk doesn't block requests made from JavaScript, and `<ClerkProvider>` shows no update screen. If you use `@clerk/expo-native-components`, the native iOS and Android SDKs still identify the app themselves, so their requests are blocked and they show their own update screen. Apps in Expo Go and web apps are never blocked.
5 changes: 5 additions & 0 deletions .changeset/expo-native-components-update-screen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/expo-native-components': patch
---

Let `@clerk/expo` turn off the native iOS and Android SDKs' own update-required screens. `<ClerkProvider>` turns them off when it shows its own update screen, or when you set `updateRequiredViewEnabled={false}`, so an app whose version is older than its minimum sees one screen. Older versions of `@clerk/expo`, and apps without `expo-application`, which `@clerk/expo` needs to identify the app, keep the native screen.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ClerkExpoModule : Module() {
emit = { sendEvent(NATIVE_CLIENT_INVALIDATED_EVENT, emptyMap<String, Any?>()) }
)
private var configuredProxyUrl: String? = null
@Volatile private var updateRequiredViewEnabled = true

private data class AuthFlowStateSnapshot(
val isLoaded: Boolean,
Expand Down Expand Up @@ -80,6 +81,10 @@ class ClerkExpoModule : Module() {
configureNative(pubKey, seedDeviceToken, proxyUrl, promise)
}

AsyncFunction("setUpdateRequiredViewEnabled") { enabled: Boolean ->
updateRequiredViewEnabled = enabled
}

AsyncFunction("getDeviceToken") { promise: Promise ->
getDeviceToken(promise)
}
Expand Down Expand Up @@ -110,8 +115,8 @@ class ClerkExpoModule : Module() {
}

// JS owns client state. The native foreground refresh races SSO completion and mints duplicate clients (#9217).
return ClerkConfigurationOptions(proxyUrl = proxyUrl)
.withForegroundRefreshDisabled()
val options = ClerkConfigurationOptions(proxyUrl = proxyUrl).withForegroundRefreshDisabled()
return (if (updateRequiredViewEnabled) options else options.withUpdateRequiredViewDisabled())
.withCustomHeaders(customHeaders)
}

Expand Down
4 changes: 2 additions & 2 deletions packages/expo-native-components/ios/ClerkExpo.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ else
end

clerk_ios_repo = 'https://github.com/clerk/clerk-ios.git'
clerk_ios_version = '1.6.2'
clerk_ios_revision = '1e6edcbc483a9ae02513eb5f28310b4db3e6e42b'

Pod::Spec.new do |s|
s.name = 'ClerkExpo'
Expand All @@ -44,7 +44,7 @@ Pod::Spec.new do |s|
spm_dependency(
s,
url: clerk_ios_repo,
requirement: { :kind => 'exactVersion', :version => clerk_ios_version },
requirement: { :kind => 'revision', :revision => clerk_ios_revision },
products: ['ClerkKit', 'ClerkKitUI']
)
else
Expand Down
7 changes: 7 additions & 0 deletions packages/expo-native-components/ios/ClerkExpoModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ public class ClerkExpoModule: Module {
}
}

AsyncFunction("setUpdateRequiredViewEnabled") { (enabled: Bool, promise: Promise) in
Task { @MainActor in
ClerkNativeBridge.setUpdateRequiredViewEnabled(enabled)
promise.resolve()
}
}

AsyncFunction("getDeviceToken") { (promise: Promise) in
Task { @MainActor in
promise.resolve(ClerkNativeBridge.shared.getDeviceToken())
Expand Down
15 changes: 13 additions & 2 deletions packages/expo-native-components/ios/ClerkNativeBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ final class ClerkNativeBridge {
private static var clerkConfigured = false
private static var configuredPublishableKey: String?
private static var configuredProxyUrl: String?
private static var updateRequiredViewEnabled = true

/// Parsed light and dark themes from Info.plist "ClerkTheme" dictionary.
var lightTheme: ClerkTheme?
Expand Down Expand Up @@ -672,9 +673,19 @@ final class ClerkNativeBridge {
private static func makeClerkOptions(proxyUrl: String?) -> Clerk.Options {
let middleware = Clerk.Options.MiddlewareConfig(request: [ClerkExpoHeaderMiddleware()])
guard let service = keychainService else {
return .init(proxyUrl: proxyUrl, middleware: middleware)
return .init(proxyUrl: proxyUrl, middleware: middleware, updateRequiredViewEnabled: updateRequiredViewEnabled)
}
return .init(keychainConfig: .init(service: service), proxyUrl: proxyUrl, middleware: middleware)
return .init(
keychainConfig: .init(service: service),
proxyUrl: proxyUrl,
middleware: middleware,
updateRequiredViewEnabled: updateRequiredViewEnabled
)
}

@MainActor
static func setUpdateRequiredViewEnabled(_ enabled: Bool) {
updateRequiredViewEnabled = enabled
}

@MainActor
Expand Down
5 changes: 5 additions & 0 deletions packages/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"@types/base-64": "^1.0.2",
"esbuild": "^0.28.1",
"expo-apple-authentication": "^8.0.8",
"expo-application": "^7.0.8",
"expo-auth-session": "^7.0.11",
"expo-constants": "^18.0.13",
"expo-crypto": "^15.0.9",
Expand All @@ -154,6 +155,7 @@
"@clerk/expo-passkeys": ">=0.0.6",
"expo": "catalog:peer-expo",
"expo-apple-authentication": ">=7.0.0",
"expo-application": ">=5.0.0",
"expo-auth-session": ">=5",
"expo-constants": ">=12",
"expo-crypto": ">=12",
Expand All @@ -180,6 +182,9 @@
"expo-apple-authentication": {
"optional": true
},
"expo-application": {
"optional": true
},
"expo-auth-session": {
"optional": true
},
Expand Down
Loading
Loading