Skip to main content
Articles

Clerk Compatibility in Expo 54 and 55

Author: Roy Anger
Published: (last updated )

What does Clerk support in Expo SDK 54 and 55?

@clerk/expo v3.1.x provides comprehensive support for Expo SDK 54 and 55, including the New Architecture and multiple authentication approaches. Part 1 of this two-part series covers version requirements, architecture integration, Expo Go constraints, and core authentication methods (Google, Apple, SSO, Passkeys, and Biometric).

@clerk/expo v3.1.x fully supports both Expo SDK 54 and Expo SDK 55 for iOS and Android. This article provides a comprehensive compatibility reference for developers integrating Clerk authentication into Expo apps, covering version requirements, authentication approaches, feature availability, setup configuration, and known limitations. All information reflects @clerk/expo v3.1.12, Expo SDK 54, and Expo SDK 55 as of April 2026.

Note

Current SDK version: @clerk/expo v3.x is part of Clerk Core 3, released March 3, 2026. The package was renamed from @clerk/clerk-expo to @clerk/expo. publishableKey is now a required prop on <ClerkProvider>. The <Show> component replaces <SignedIn>, <SignedOut>, and <Protect>. Hooks were reorganized into subpath imports (e.g., @clerk/expo/apple, @clerk/expo/google). This article covers the current SDK version and is not a migration guide. If upgrading from v2.x, refer to the Core 3 upgrade guide.

Clerk and Expo Compatibility: Version Support Matrix

The @clerk/expo SDK v3.1.x is compatible with both Expo SDK 54 and Expo SDK 55. The following table summarizes the version requirements for each component.

ComponentExpo SDK 54Expo SDK 55
@clerk/expo version3.1.x3.1.x
React Native0.810.83
React19.119.2
New ArchitectureSupported (optional)Required (mandatory)
Legacy ArchitectureSupported (final SDK)Not available
Minimum Xcode16.1 (26 recommended)26
EAS Build default Xcode26.026.2
Android targetSdkVersion36 (API 36)36 (API 36)
Node.js>=20.9.0>=20.9.0

Note

Xcode versioning: Apple adopted year-based versioning at WWDC 2025, jumping from Xcode 16 directly to Xcode 26. Versions 17 through 25 do not exist. Xcode 26.0 GA shipped September 15, 2025, and Xcode 26.4 is the current stable release as of April 2026.

The @clerk/expo package declares the following peer dependencies: expo: >=53 <56, react: ^18 || ^19, and react-native: >=0.73. The minimum Node.js requirement is 20.9.0.

All three Clerk authentication approaches work on both SDK versions. The primary differences between SDK 54 and SDK 55 are:

  • SDK 54 supports both the Legacy Architecture and the New Architecture. It is the last SDK to support the Legacy Architecture.
  • SDK 55 requires the New Architecture. The newArchEnabled configuration option has been removed.

Passkeys are supported on both SDK versions — @clerk/expo-passkeys declares a peer dependency of expo: >=53 <57 (as of v1.1.0), which includes both SDK 54 and SDK 55.

How Clerk Integrates with Expo

Architecture Overview

The @clerk/expo package builds on top of @clerk/react, which wraps ClerkJS — the core JavaScript SDK. When you add <ClerkProvider> to your Expo app, it initializes the authentication context and connects to Clerk's Frontend API (FAPI) using your publishable key.

Clerk uses a hybrid stateful and stateless session model. Each session is stored in Clerk's database and represented on the client as a short-lived JSON Web Token (JWT) with a 60-second expiry. The SDK automatically refreshes this token on a 50-second interval, ensuring uninterrupted access without manual token management.

In Expo apps, token persistence is handled via the tokenCache prop on <ClerkProvider>. Using expo-secure-store, tokens are encrypted and stored on the device — in Apple Keychain on iOS and Android Keystore on Android. Without tokenCache, tokens are stored in memory and lost when the app restarts.

The publishable key (environment variable EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY) identifies your application and encodes the FAPI URL. This key must be explicitly passed as a prop to <ClerkProvider> in Expo because React Native production builds do not inline environment variables the same way web bundlers do.

Three Approaches to Clerk Authentication in Expo

Clerk organizes Expo authentication into three approaches, each adding capability and requiring more native integration.

Approach 1: JavaScript Custom Flows

JavaScript custom flows use only JavaScript-based authentication with no native module dependencies. This includes email/password sign-in and sign-up, phone verification via OTP, magic links (email links), and passwordless login. The relevant hooks are useSignIn(), useSignUp(), useAuth(), useUser(), and useSession().

JavaScript custom flows work in both Expo Go and development builds. This approach is suitable for rapid prototyping and for providers that do not have native SDKs.

Approach 2: JavaScript + Native Sign-In Hooks

This approach adds native platform integration for specific authentication methods:

  • Native Google Sign-In via useSignInWithGoogle (from @clerk/expo/google) — uses the platform's system credential picker (ASAuthorization on iOS, Credential Manager on Android) without browser redirects
  • Native Apple Sign-In via useSignInWithApple (from @clerk/expo/apple) — uses Apple's native authentication UI on iOS
  • SSO/OAuth via useSSO() — browser-based social login supporting 31+ providers, requiring a custom URL scheme for redirects
  • Biometric authentication via useLocalCredentials (from @clerk/expo/local-credentials) — stores password credentials with biometric unlock

Approach 2 requires development builds and does not work in Expo Go because Expo Go cannot load custom native modules or register custom URL schemes.

Approach 3: Native Components (Beta)

Native components render fully native UI using SwiftUI on iOS (via the clerk-ios SDK) and Jetpack Compose on Android (via the clerk-android SDK). Three components are available:

  • <AuthView /> — complete sign-in/sign-up UI that automatically handles all authentication methods enabled in the Clerk Dashboard
  • <UserButton /> — avatar that opens a native profile modal on tap
  • <UserProfileView /> — inline profile management (email, phone, MFA, passkeys, sessions, connected accounts)

Native components were released in beta on March 9, 2026 as part of @clerk/expo v3.1.0. They require development builds and the @clerk/expo plugin in app.json. Approach 1 and Approach 2 remain the production-stable options.

Expo SDK 54 Compatibility

Expo 54 at a Glance

Expo SDK 54 was released on September 10, 2025. It ships React Native 0.81 and React 19.1.

SDK 54 is the last SDK version to support the Legacy Architecture. Both the Legacy Architecture and the New Architecture work in SDK 54. At the time of SDK 54's release, approximately 75% of projects on EAS Build were already using the New Architecture.

Key platform changes in SDK 54:

  • Precompiled XCFrameworks for faster iOS builds — clean build times dropped from approximately 120 seconds to approximately 10 seconds on M4 Max hardware
  • Rebuilt expo-dev-launcher with improved debugging capabilities
  • Android 16 / API 36 is the default targetSdkVersion, making edge-to-edge display mandatory
  • Minimum Xcode 16.1 required (Xcode 26 recommended)

Clerk Feature Support on Expo 54

@clerk/expo v3.1.x provides full support for all three authentication approaches on Expo SDK 54:

  • Approach 1 (JavaScript custom flows): Fully supported in both Expo Go and development builds
  • Approach 2 (JavaScript + native hooks): Fully supported in development builds
  • Approach 3 (Native components): Supported in beta in development builds

Both the Legacy Architecture and the New Architecture are compatible with @clerk/expo. The SDK v3.1.5 release added the -Xskip-metadata-version-check Kotlin compiler flag for SDK 54 and SDK 55 compatibility, and fixed an Android New Architecture codegen error related to the NativeClerkModule.

Token caching with expo-secure-store works as expected on SDK 54. Passkeys are supported via @clerk/expo-passkeys, which includes SDK 54 in its peer dependency range (expo: >=53 <57).

There are no known Clerk-specific caveats unique to SDK 54.

Tip

If your project is still using the Legacy Architecture, SDK 54 is the last opportunity to upgrade to the New Architecture before SDK 55 makes it mandatory. Expo recommends upgrading to the New Architecture on SDK 54 first, testing, then upgrading the SDK version — rather than doing both simultaneously.

Expo SDK 55 Compatibility

Expo 55 at a Glance

Expo SDK 55 was released on February 25, 2026. It ships React Native 0.83 and React 19.2, which introduces the Activity component and the useEffectEvent hook.

The most significant change in SDK 55 is that the New Architecture is mandatory. The newArchEnabled configuration option has been removed and the Legacy Architecture is no longer available. Approximately 83% of SDK 54 projects on EAS Build were already using the New Architecture before SDK 55 shipped.

Other notable changes in SDK 55:

  • Hermes v1 available as an opt-in JavaScript engine. Enable it by setting useHermesV1: true and buildReactNativeFromSource: true in the expo-build-properties plugin, and overriding the hermes-compiler version in package.json. Hermes v1 offers meaningful performance improvements and better support for modern JavaScript features. Caveat: it requires building React Native from source, which significantly increases native build times. It is not yet recommended for Android in monorepo projects.
  • Bytecode diffing for OTA updates — approximately 75% smaller update downloads
  • Minimum Xcode 26 required (see Xcode versioning note in the Version Support Matrix)
  • Native Tabs API and Apple Zoom transitions for enhanced navigation
  • All Expo SDK packages now use matching major version numbers (e.g., expo-camera@^55.0.0)

Important

Legacy Architecture transition: As of April 2026, Expo Go in the App Store and Play Store remains on SDK 54. To use SDK 55 with Expo Go, install it via Expo CLI on Android, the TestFlight External Beta for iOS, or build a custom Expo Go via eas go. Additionally, npx create-expo-app defaults to SDK 54 during the transition period. To create an SDK 55 project, use npx create-expo-app@latest --template default@sdk-55.

Clerk Feature Support on Expo 55

@clerk/expo v3.1.0 added explicit Expo SDK 55 support by updating its peer dependency to expo: >=53 <56. All three authentication approaches are confirmed working:

  • Approach 1 (JavaScript custom flows): Fully supported
  • Approach 2 (JavaScript + native hooks): Fully supported
  • Approach 3 (Native components): Supported in beta

The New Architecture is fully compatible with @clerk/expo. TurboModules and the Fabric renderer work without issues.

Passkeys are supported on SDK 55. @clerk/expo-passkeys v1.1.0 expanded its peer dependency to expo: >=53 <57, which formally includes Expo SDK 55. Upgrade to the latest @clerk/expo-passkeys release; versions up to v1.0.13 capped support at expo: >=53 <55 and excluded SDK 55. See the Passkeys section for configuration details.

Several open GitHub issues affect SDK 55 users. See the Known Issues and Limitations section for current status.

New Architecture Impact on Clerk Authentication

The mandatory New Architecture in SDK 55 requires no action from Clerk users. The @clerk/expo package uses expo-modules-core for native module integration, which supports the New Architecture by default.

Clerk's native components use JSI-based TurboModules for JavaScript-to-native communication. The Fabric renderer is fully compatible — no rendering issues have been reported.

React Native 0.83 introduced the option to compile out Legacy Architecture code entirely by setting RCT_REMOVE_LEGACY_ARCH=1. This produces approximately 20% faster iOS builds and approximately 6% smaller app size. This optimization is compatible with Clerk.

TurboModules also improve performance for Clerk operations by lazy-loading native modules on demand rather than eagerly loading them at startup, which reduces cold-start memory usage.

Expo Go vs. Development Builds

What Works in Expo Go

Expo Go supports Approach 1 only — JavaScript custom flows. The following features work in Expo Go:

  • Email/password sign-in and sign-up
  • Phone verification (OTP)
  • Magic links
  • Session management: useAuth(), useUser(), useSession()
  • Conditional rendering: <Show when="signed-in">, <Show when="signed-out">
  • Loading states: <ClerkLoaded>, <ClerkLoading>
  • Token caching with expo-secure-store
  • Organizations, RBAC, and user management hooks

The following features do not work in Expo Go: social OAuth (useSSO()), native Google Sign-In, native Apple Sign-In, native components, passkeys, and biometric sign-in. Expo Go cannot register custom URL schemes (required for OAuth redirects) and cannot load custom native modules.

What Requires a Development Build

A development build is required for:

  • Social OAuth via useSSO() — custom URL scheme redirect required
  • Native Google Sign-In via useSignInWithGoogle
  • Native Apple Sign-In via useSignInWithApple
  • Native components (<AuthView />, <UserButton />, <UserProfileView />)
  • Passkeys via @clerk/expo-passkeys
  • Biometric sign-in via useLocalCredentials
  • Custom URL scheme registration for deep linking

Choosing the Right Environment

ScenarioRecommended Environment
Prototyping with email/passwordExpo Go
Testing basic Clerk integrationExpo Go
Social login (Google, GitHub, etc.)Development Build
Native Google or Apple Sign-InDevelopment Build
Using native componentsDevelopment Build
Production deploymentDevelopment Build
Passkeys or biometric authDevelopment Build

Start with Expo Go for initial setup and email/password flows. Switch to a development build when adding social or native authentication.

Two ways to create a development build:

  • Local build (npx expo run:ios / npx expo run:android): Compiles using locally installed Xcode (iOS, macOS only) or Android Studio. Best for rapid iteration — rebuilds only changed native code on subsequent runs. No EAS account required. Note: a paid Apple Developer Program membership ($99/year) is effectively required for Apple Sign-In entitlement configuration, Associated Domains (needed for passkeys), and App Store distribution.
  • EAS Build (eas build --profile development): Builds on remote EAS servers with no local native tooling required. Can build iOS from Windows or Linux. Handles credential management (certificates, provisioning profiles) automatically. Best for team builds, CI/CD, and distribution.

A new development build is required whenever native configuration changes (adding a URL scheme, passkey support, native sign-in, or plugins). JavaScript-only changes load via the dev server without rebuilding.

Note

Expo Go is not recommended for production apps. This is Expo's own guidance.

Authentication Methods in Detail

Native Google Sign-In

Native Google Sign-In provides a platform-native credential picker on both iOS and Android, with no browser redirect:

  • iOS: Uses ASAuthorization — the system credential picker that appears natively
  • Android: Uses Credential Manager — a system bottom sheet with one-tap support

Prerequisites:

  1. A development build (does not work in Expo Go)
  2. Clerk Dashboard: Register your native app — Team ID + Bundle ID for iOS, package name + SHA-256 fingerprint for Android
  3. Google Cloud Console: Create OAuth 2.0 credentials — an iOS Client ID, an Android Client ID, and a Web Application Client ID (the web client is required even for native apps)
  4. Environment variables: EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID, EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID (iOS), EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME (iOS)
  5. Add the @clerk/expo plugin to app.json (see Plugin Configuration)
  6. Install peer dependency: expo-crypto

Warning

Android SHA-256 fingerprint mismatch is a common pitfall. Three different fingerprints exist: the debug keystore fingerprint, the EAS managed keystore fingerprint (get it via eas credentials -p android), and the Google Play App Signing key fingerprint (find it in Release > Setup > App Signing in the Play Console). Register the correct fingerprint for each environment in the Clerk Dashboard.

import { useSignInWithGoogle } from '@clerk/expo/google'
import { Pressable, Text } from 'react-native'

export function GoogleSignInButton() {
  const { startGoogleAuthenticationFlow } = useSignInWithGoogle()

  const onPress = async () => {
    try {
      const { createdSessionId, setActive } = await startGoogleAuthenticationFlow()

      if (createdSessionId) {
        await setActive({ session: createdSessionId })
      }
    } catch (err) {
      console.error('Google sign-in error:', err)
    }
  }

  return (
    <Pressable onPress={onPress}>
      <Text>Sign in with Google</Text>
    </Pressable>
  )
}

For detailed setup instructions including Google Cloud Console configuration, see Configure native Google Sign-In for Expo.

Native Apple Sign-In

Native Apple Sign-In uses Apple's authentication UI on iOS. It is iOS only — the hook returns null on non-iOS platforms. If your app offers social sign-in alongside another provider (e.g., Google), Apple may require "Sign in with Apple" for App Store approval.

Native Apple Sign-In supports Apple's Hide My Email privacy feature automatically.

Prerequisites:

  1. A development build
  2. Install peer dependencies: expo-apple-authentication, expo-crypto
  3. Clerk Dashboard: Register App ID Prefix (Team ID) + Bundle ID

When using native components, Apple Sign-In is automatically available in <AuthView /> when Apple is enabled in the Clerk Dashboard. For a custom UI, use the useSignInWithApple hook:

import { useSignInWithApple } from '@clerk/expo/apple'
import { Platform, Pressable, Text } from 'react-native'

export function AppleSignInButton() {
  const signInWithApple = useSignInWithApple()

  if (!signInWithApple || Platform.OS !== 'ios') return null

  const onPress = async () => {
    try {
      const { createdSessionId, setActive } = await signInWithApple.startAppleAuthenticationFlow()

      if (createdSessionId) {
        await setActive({ session: createdSessionId })
      }
    } catch (err) {
      console.error('Apple sign-in error:', err)
    }
  }

  return (
    <Pressable onPress={onPress}>
      <Text>Sign in with Apple</Text>
    </Pressable>
  )
}

For detailed configuration, see Configure native Apple Sign-In for Expo.

Browser-Based SSO

useSSO() opens the system browser for OAuth and enterprise SSO flows. It replaces the deprecated useOAuth() hook — all new code should use useSSO().

The browser experience uses ASWebAuthenticationSession on iOS and Chrome Custom Tabs on Android. useSSO() supports 31+ social providers (Google, GitHub, Discord, LinkedIn, and more) as well as enterprise SSO protocols (SAML, OIDC, EASIE).

useSSO() requires a development build. Expo Go cannot register custom URL schemes, which are required for the post-authentication redirect back to the app.

Key parameters:

  • strategy: 'oauth_<provider>' for social login, or 'enterprise_sso' for enterprise SSO
  • identifier: Required for enterprise SSO to identify the connection
  • redirectUrl: Generated via AuthSession.makeRedirectUri()
import { useSSO } from '@clerk/expo'
import * as AuthSession from 'expo-auth-session'
import * as WebBrowser from 'expo-web-browser'
import { Pressable, Text, Platform } from 'react-native'
import { useEffect } from 'react'

export function SSOSignInButton() {
  const { startSSOFlow } = useSSO()

  useEffect(() => {
    // Warm up the browser on Android for faster opening
    if (Platform.OS === 'android') {
      void WebBrowser.warmUpAsync()
      return () => {
        void WebBrowser.coolDownAsync()
      }
    }
  }, [])

  const onPress = async () => {
    try {
      const { createdSessionId, setActive } = await startSSOFlow({
        strategy: 'oauth_google',
        redirectUrl: AuthSession.makeRedirectUri(),
      })

      if (createdSessionId) {
        await setActive({ session: createdSessionId })
      }
    } catch (err) {
      console.error('SSO error:', err)
    }
  }

  return (
    <Pressable onPress={onPress}>
      <Text>Sign in with Google (Browser)</Text>
    </Pressable>
  )
}

Browser-based SSO offers broader provider support than native sign-in but trades UX polish for compatibility — users see a browser redirect rather than a system credential picker. For Google and Apple, native sign-in hooks provide a more seamless experience.

Passkeys

Clerk supports native passkeys in Expo via the @clerk/expo-passkeys package. Passkeys use WebAuthn to provide phishing-resistant, passwordless authentication bound to the device and domain.

  • Create a passkey: user.createPasskey()
  • Sign in with a passkey: signIn.authenticateWithPasskey()

Platform requirements:

  • iOS 16+ with Associated Domains entitlement
  • Android 9+ with a physical device (emulators are not supported)
  • Maximum 10 passkeys per account, domain-locked

Important

Android emulators do not support passkeys. This is a platform-level limitation of Android's Credential Manager implementation, not a restriction from Clerk. Clerk's official documentation states: "Passkeys will not work with Android emulators. You must use a physical device."

iOS configuration requires adding associated domains to app.json:

  • applinks:<FAPI_URL> and webcredentials:<FAPI_URL>
  • Set ios.deploymentTarget: "16.0" via the expo-build-properties plugin
  • Register App ID Prefix + Bundle ID in the Clerk Dashboard under Native Applications

Android configuration requires intent filters with autoVerify: true in app.json, pointing to your Clerk FAPI domain. Clerk hosts the assetlinks.json file on the FAPI domain — configure it via the Clerk Dashboard (Native Applications > Android), not self-hosted. Register the package name and SHA-256 fingerprints for each build environment.

// In your root layout, configure ClerkProvider with passkeys
import { ClerkProvider } from '@clerk/expo'
import { passkeys } from '@clerk/expo-passkeys'
import { tokenCache } from '@clerk/expo/token-cache'

export default function RootLayout() {
  return (
    <ClerkProvider
      publishableKey={process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!}
      tokenCache={tokenCache}
      __experimental_passkeys={passkeys}
    >
      {/* Your app */}
    </ClerkProvider>
  )
}

Warning

Passkeys are an experimental API. Passkey support is exposed through the __experimental_passkeys prop, and the __experimental_ prefix signals that this API surface may change — including breaking changes — in a future release without a major-version bump. Pin @clerk/expo-passkeys to an exact version and re-test after any upgrade before relying on passkeys in production.

In a sign-in component, authenticate with a passkey:

import { useSignIn } from '@clerk/expo'
import { Pressable, Text } from 'react-native'

export function PasskeySignIn() {
  const { signIn, setActive } = useSignIn()

  const onPress = async () => {
    try {
      const result = await signIn!.authenticateWithPasskey()
      await setActive({ session: result.createdSessionId })
    } catch (err) {
      console.error('Passkey sign-in error:', err)
    }
  }

  return (
    <Pressable onPress={onPress}>
      <Text>Sign in with Passkey</Text>
    </Pressable>
  )
}

For detailed configuration including iOS Associated Domains and Android intent filters, see Configure passkeys for Expo.

Biometric Sign-In

The useLocalCredentials hook from @clerk/expo/local-credentials enables returning users to sign in using Face ID, Touch ID, or fingerprint authentication. It works by storing the user's password credentials securely on the device after their initial sign-in, then retrieving and auto-submitting them after biometric verification.

Requirements:

  • @clerk/expo v2.2.0 or later
  • expo-local-authentication v13.5.0 or later
  • Password-based sign-in strategy enabled (does not work with OAuth-only accounts)
  • Device must have enrolled biometrics and a passcode
  • Development build required

The hook provides these key properties and methods:

  • hasCredentials: Whether saved credentials exist on this device
  • userOwnsCredentials: Whether the saved credentials belong to the current user
  • biometricType: 'face-recognition', 'fingerprint', or null
  • setCredentials(): Save the current user's password after initial sign-in
  • clearCredentials(): Remove saved credentials
  • authenticate(): Trigger biometric prompt and sign in

Credentials are automatically deleted if the device passcode is removed.

import { useLocalCredentials } from '@clerk/expo/local-credentials'
import { useSignIn } from '@clerk/expo'
import { Pressable, Text } from 'react-native'

export function BiometricSignIn() {
  const { hasCredentials, biometricType, authenticate, setCredentials } = useLocalCredentials()
  const { signIn, setActive } = useSignIn()

  if (hasCredentials && biometricType) {
    return (
      <Pressable
        onPress={async () => {
          try {
            const { createdSessionId } = await authenticate()
            if (createdSessionId) {
              await setActive({ session: createdSessionId })
            }
          } catch (err) {
            console.error('Biometric auth failed:', err)
          }
        }}
      >
        <Text>Sign in with {biometricType === 'face-recognition' ? 'Face ID' : 'Fingerprint'}</Text>
      </Pressable>
    )
  }

  // After a successful password sign-in, offer to save credentials
  // by calling setCredentials() to store for future biometric sign-in
  return null
}

For complete setup instructions, see Configure biometric sign-in for Expo.

Next Steps

This concludes Part 1 of the compatibility reference, covering version requirements, environments, and core authentication hooks. In Part 2, we explore the new beta Native Components, token caching, user and organization management, setup configuration, and production deployment considerations.

FAQ

In this series

  1. Clerk Compatibility in Expo 54 and 55 (you are here)
  2. Clerk Compatibility in Expo 54 and 55 - Part 2