Skip to content

feat(expo): align native prebuilt components with mobile SDKs#8699

Open
mikepitre wants to merge 42 commits into
mainfrom
mike/expo-native-prebuilt-parity
Open

feat(expo): align native prebuilt components with mobile SDKs#8699
mikepitre wants to merge 42 commits into
mainfrom
mike/expo-native-prebuilt-parity

Conversation

@mikepitre
Copy link
Copy Markdown
Contributor

@mikepitre mikepitre commented May 28, 2026

Summary

This PR brings Expo's native prebuilt components closer to the iOS and Android SDKs:

  • AuthView and UserProfileView are now renderable content. App code owns whether they appear in a modal, sheet, route, or full-screen root.
  • UserButton is backed by the platform-native Clerk button instead of a JS avatar/button implementation.
  • Native and JS auth state sync by refreshing both SDK clients toward the Clerk backend client, not by mirroring sign-out or interpreting component-level auth events.
  • Android native registration moved to Expo Modules. iOS stays on the existing app-target bridge until Expo has a clean SPM-compatible path for this setup.

Updated Docs PR clerk/clerk-docs#3424

What Changed

Prebuilt Views Are App-Presented Content

Before: Expo owned presentation through imperative helpers and Android activities, and profile presentation used useUserProfileModal().

const { presentUserProfile } = useUserProfileModal();

<AuthView isDismissable={false} />;
<Button title="Manage account" onPress={presentUserProfile} />;

After: AuthView and UserProfileView render as content. Apps present them in their own modal, sheet, route, or root view.

<Modal visible={open} presentationStyle="pageSheet" onRequestClose={() => setOpen(false)}>
  <AuthView onDismiss={() => setOpen(false)} />
</Modal>

<UserProfileView onDismiss={() => setOpen(false)} />
<AuthView isDismissible={false} />

Motivation: This matches the native SDK model: the prebuilt component owns its content and built-in dismissal affordance, while the app owns presentation.

UserButton Is The Native Button Surface

Before: UserButton was a JS-managed avatar/profile launcher and accepted JS layout props.

<UserButton style={{ width: 40, height: 40 }} />

After: UserButton wraps the platform-native Clerk button directly.

<UserButton />

Motivation: The Expo API now mirrors the mobile SDKs: tapping the native button opens the native user profile, and Expo does not reimplement avatar/profile behavior in JS.

Native And JS Sync Through Client Refresh

Before: Native prebuilt views emitted auth-specific events such as sign-in/sign-out completion, and Expo JS interpreted those events or mirrored sign-out across SDKs.

After: Both sides refresh toward the Clerk backend client:

  • Native-to-JS: iOS observes Clerk iOS client changes; Android observes Clerk.clientFlow; both emit an internal refreshClient event.
  • JS-to-native: ClerkProvider observes Clerk JS resource changes, syncs the current JS client token to native when one exists, and otherwise asks the native SDK to refresh its current client.

Motivation: The backend client endpoint stays the source of truth. Expo no longer needs component-level auth semantics, duplicate sign-out calls, or one-off session syncing paths.

Android Uses Expo Modules

Before: Android used React Native package/view-manager registration plus presentation activities/factory paths for the prebuilt surfaces.

After: Android registers the Clerk module and native prebuilt views through Expo Modules. JS uses requireNativeModule and requireNativeView for Android.

Motivation: Expo Modules are the cleaner fit for Android now. iOS intentionally stays on the existing React Native bridge plus app-target native bridge because Clerk iOS is supplied to the consuming app through SPM, and the CocoaPods-backed Expo module target cannot cleanly import that app-target SPM dependency yet.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 28, 2026

🦋 Changeset detected

Latest commit: 4282c3d

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

This PR includes changesets to release 1 package
Name Type
@clerk/expo Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 5, 2026 4:26pm

Request Review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 28, 2026

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8699

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 4282c3d

Comment thread packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt Outdated
Comment thread packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserButtonExpoView.kt Outdated
Comment thread packages/expo/android/src/main/java/expo/modules/clerk/ClerkUserButtonExpoView.kt Outdated
chriscanin

This comment was marked as outdated.

Comment thread packages/expo/ios/ClerkExpoModule.swift Outdated
Comment thread packages/expo/ios/ClerkExpoModule.swift Outdated
Comment thread packages/expo/ios/ClerkExpoModule.swift Outdated
Comment thread packages/expo/ios/ClerkExpoModule.swift Outdated
@clerk clerk deleted a comment from chriscanin Jun 1, 2026
@clerk clerk deleted a comment from chriscanin Jun 1, 2026
@clerk clerk deleted a comment from chriscanin Jun 1, 2026
@swolfand swolfand closed this Jun 1, 2026
@swolfand swolfand reopened this Jun 1, 2026
Copy link
Copy Markdown
Member

@wobsoriano wobsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left minor suggestions and one bug I encountered on iOS (havent tested Android)

Comment thread .changeset/quiet-android-bridge.md
Comment thread .changeset/remove-expo-present-auth.md Outdated
Comment thread .changeset/bump-expo-native-sdks.md
Comment thread packages/expo/src/provider/ClerkProvider.tsx
Comment thread packages/expo/src/native/AuthView.tsx
Comment thread packages/expo/app.plugin.js
mikepitre and others added 2 commits June 5, 2026 11:39
Co-authored-by: Robert Soriano <sorianorobertc@gmail.com>
Copy link
Copy Markdown
Member

@wobsoriano wobsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for improving and fixing the native components! 👍🏼

Attaching my local testing for reference

Screen.Recording.2026-06-05.at.9.34.14.AM.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants