diff --git a/.changeset/expo-organization-switcher.md b/.changeset/expo-organization-switcher.md
new file mode 100644
index 00000000000..a2289cd8432
--- /dev/null
+++ b/.changeset/expo-organization-switcher.md
@@ -0,0 +1,21 @@
+---
+'@clerk/expo': minor
+---
+
+Add `` native component to `@clerk/expo/native`.
+
+Renders a fully native organization switcher inline in your React Native view hierarchy — SwiftUI on iOS and Jetpack Compose on Android — backed by the prebuilt `OrganizationSwitcher` views in `clerk-ios` and `clerk-android`. Tapping the switcher opens the platform's native overview / account-list / manage sheets, including the full `OrganizationProfileView` flow (members, domains, invitations, action confirmations) on platforms where it has shipped.
+
+```tsx
+import { OrganizationSwitcher } from '@clerk/expo/native'
+
+ {
+ // re-query useOrganization() or refetch as needed
+ }}
+/>
+```
+
+Active-organization changes are surfaced via the `onOrganizationChanged` callback so consumers can react in JS.
+
+Requires the underlying native SDKs to include their `OrganizationSwitcher` prebuilt views — `clerk-ios` ≥ the release containing [clerk/clerk-ios#411](https://github.com/clerk/clerk-ios/pull/411) and `clerk-android` ≥ 1.0.17.
diff --git a/packages/expo/android/build.gradle b/packages/expo/android/build.gradle
index d860cd02919..2452939d3db 100644
--- a/packages/expo/android/build.gradle
+++ b/packages/expo/android/build.gradle
@@ -18,8 +18,14 @@ ext {
credentialsVersion = "1.3.0"
googleIdVersion = "1.1.1"
kotlinxCoroutinesVersion = "1.7.3"
- clerkAndroidApiVersion = "1.0.16"
- clerkAndroidUiVersion = "1.0.16"
+ // OrganizationSwitcher + OrganizationProfileView surface depends on the
+ // clerk-android org suite merged 2026-05-13..16 (PRs #628–#638). v1.0.16
+ // (2026-05-11) predates that work — bump to v1.0.17 once tagged. For
+ // local testing against unreleased main, publish a snapshot via
+ // `./gradlew publishToMavenLocal` from a clerk-android worktree and pin
+ // these to your snapshot version.
+ clerkAndroidApiVersion = "1.0.17"
+ clerkAndroidUiVersion = "1.0.17"
composeVersion = "1.7.0"
activityComposeVersion = "1.9.0"
lifecycleVersion = "2.8.0"
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherExpoView.kt b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherExpoView.kt
new file mode 100644
index 00000000000..b10e24ab52e
--- /dev/null
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherExpoView.kt
@@ -0,0 +1,148 @@
+package expo.modules.clerk
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.Log
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.AndroidUiDispatcher
+import androidx.compose.ui.platform.ComposeView
+import androidx.lifecycle.compose.LocalLifecycleOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeViewModelStoreOwner
+import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
+import androidx.savedstate.compose.LocalSavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.clerk.api.Clerk
+import com.clerk.ui.organizationswitcher.OrganizationSwitcher
+import com.facebook.react.bridge.Arguments
+import com.facebook.react.bridge.ReactContext
+import com.facebook.react.uimanager.events.RCTEventEmitter
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+private const val TAG = "ClerkOrgSwitcherView"
+
+class ClerkOrganizationSwitcherNativeView(context: Context) : FrameLayout(context) {
+ var hidePersonal: Boolean = false
+
+ private val activity = ClerkAuthNativeView.findActivity(context)
+
+ private var recomposer: Recomposer? = null
+ private var recomposerJob: kotlinx.coroutines.Job? = null
+
+ private val composeView = ComposeView(context).also { view ->
+ activity?.let { act ->
+ view.setViewTreeLifecycleOwner(act)
+ view.setViewTreeViewModelStoreOwner(act)
+ view.setViewTreeSavedStateRegistryOwner(act)
+
+ val recomposerContext = AndroidUiDispatcher.Main
+ val newRecomposer = Recomposer(recomposerContext)
+ recomposer = newRecomposer
+ view.setParentCompositionContext(newRecomposer)
+ val scope = CoroutineScope(recomposerContext + kotlinx.coroutines.SupervisorJob())
+ recomposerJob = scope.coroutineContext[kotlinx.coroutines.Job]
+ scope.launch {
+ newRecomposer.runRecomposeAndApplyChanges()
+ }
+ }
+ addView(view, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
+ }
+
+ override fun onDetachedFromWindow() {
+ recomposer?.cancel()
+ recomposerJob?.cancel()
+ super.onDetachedFromWindow()
+ }
+
+ fun setupView() {
+ Log.d(TAG, "setupView - hidePersonal: $hidePersonal")
+
+ composeView.setContent {
+ // Track the last-known active organization id so we only emit the JS event
+ // when it actually changes. The native composable's onOrganizationChanged
+ // callback is a Unit lambda; we read the new id off the session flow.
+ val session by Clerk.sessionFlow.collectAsStateWithLifecycle()
+ var lastOrgId by remember { mutableStateOf(Clerk.session?.lastActiveOrganizationId) }
+
+ LaunchedEffect(session?.lastActiveOrganizationId) {
+ val currentId = session?.lastActiveOrganizationId
+ if (currentId != lastOrgId) {
+ lastOrgId = currentId
+ sendEvent("organizationChanged", mapOf("organizationId" to currentId))
+ }
+ }
+
+ val content = @androidx.compose.runtime.Composable {
+ // React Native apps don't follow Android system dark mode by default,
+ // so the surrounding RN UI renders light even when the OS is in
+ // dark mode. ClerkMaterialTheme's color picker reads
+ // isSystemInDarkTheme() — pin LocalConfiguration's night flag off
+ // so it matches what the host app actually displays.
+ // TODO: surface a `colorScheme` prop so consumers can opt into
+ // following the host app's Appearance API state instead.
+ val baseConfig = LocalConfiguration.current
+ val lightConfig = remember(baseConfig) {
+ Configuration(baseConfig).apply {
+ uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or
+ Configuration.UI_MODE_NIGHT_NO
+ }
+ }
+ CompositionLocalProvider(LocalConfiguration provides lightConfig) {
+ OrganizationSwitcher(
+ modifier = Modifier.fillMaxWidth(),
+ clerkTheme = Clerk.customTheme,
+ // Consumers should compose from @clerk/expo/native
+ // separately when they want one — keeps parity with iOS, which
+ // doesn't bundle a UserButton in its OrganizationSwitcher.
+ showUserButton = false,
+ onOrganizationChanged = {
+ // The composable signals completion; we read the new id off
+ // the session flow via the LaunchedEffect above to get an
+ // actual id payload.
+ Log.d(TAG, "Org switch completed")
+ },
+ )
+ }
+ }
+
+ if (activity != null) {
+ CompositionLocalProvider(
+ LocalViewModelStoreOwner provides activity,
+ LocalLifecycleOwner provides activity,
+ LocalSavedStateRegistryOwner provides activity,
+ ) {
+ content()
+ }
+ } else {
+ content()
+ }
+ }
+ }
+
+ private fun sendEvent(type: String, data: Map) {
+ val reactContext = context as? ReactContext ?: return
+ val eventData = Arguments.createMap().apply {
+ putString("type", type)
+ val jsonString = try {
+ org.json.JSONObject(data).toString()
+ } catch (e: Exception) {
+ "{}"
+ }
+ putString("data", jsonString)
+ }
+ reactContext.getJSModule(RCTEventEmitter::class.java)
+ .receiveEvent(id, "onOrganizationEvent", eventData)
+ }
+}
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherViewManager.kt b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherViewManager.kt
new file mode 100644
index 00000000000..7ca80d75210
--- /dev/null
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkOrganizationSwitcherViewManager.kt
@@ -0,0 +1,32 @@
+package expo.modules.clerk
+
+import com.facebook.react.common.MapBuilder
+import com.facebook.react.uimanager.SimpleViewManager
+import com.facebook.react.uimanager.ThemedReactContext
+import com.facebook.react.uimanager.annotations.ReactProp
+import com.facebook.react.viewmanagers.ClerkOrganizationSwitcherViewManagerInterface
+
+class ClerkOrganizationSwitcherViewManager : SimpleViewManager(),
+ ClerkOrganizationSwitcherViewManagerInterface {
+
+ override fun getName(): String = "ClerkOrganizationSwitcherView"
+
+ override fun createViewInstance(reactContext: ThemedReactContext): ClerkOrganizationSwitcherNativeView {
+ return ClerkOrganizationSwitcherNativeView(reactContext)
+ }
+
+ @ReactProp(name = "hidePersonal")
+ override fun setHidePersonal(view: ClerkOrganizationSwitcherNativeView, hidePersonal: Boolean) {
+ view.hidePersonal = hidePersonal
+ view.setupView()
+ }
+
+ override fun getExportedCustomBubblingEventTypeConstants(): MutableMap? {
+ return MapBuilder.builder()
+ .put("onOrganizationEvent", MapBuilder.of(
+ "phasedRegistrationNames",
+ MapBuilder.of("bubbled", "onOrganizationEvent")
+ ))
+ .build() as MutableMap
+ }
+}
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkPackage.kt b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkPackage.kt
index 9a97309ac5e..10a71a436d1 100644
--- a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkPackage.kt
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkPackage.kt
@@ -38,6 +38,7 @@ class ClerkPackage : TurboReactPackage() {
return listOf(
ClerkAuthViewManager(),
ClerkUserProfileViewManager(),
+ ClerkOrganizationSwitcherViewManager(),
)
}
}
diff --git a/packages/expo/ios/ClerkExpo.podspec b/packages/expo/ios/ClerkExpo.podspec
index fbd91f9a91c..8e3d74aec15 100644
--- a/packages/expo/ios/ClerkExpo.podspec
+++ b/packages/expo/ios/ClerkExpo.podspec
@@ -40,7 +40,8 @@ Pod::Spec.new do |s|
# because it uses `import ClerkKit` which is only available via SPM in the app target.
s.source_files = "ClerkExpoModule.swift", "ClerkExpoModule.m",
"ClerkAuthViewManager.swift", "ClerkAuthViewManager.m",
- "ClerkUserProfileViewManager.swift", "ClerkUserProfileViewManager.m"
+ "ClerkUserProfileViewManager.swift", "ClerkUserProfileViewManager.m",
+ "ClerkOrganizationSwitcherViewManager.swift", "ClerkOrganizationSwitcherViewManager.m"
install_modules_dependencies(s)
end
diff --git a/packages/expo/ios/ClerkExpoModule.swift b/packages/expo/ios/ClerkExpoModule.swift
index efd1e142445..5efd6ffd951 100644
--- a/packages/expo/ios/ClerkExpoModule.swift
+++ b/packages/expo/ios/ClerkExpoModule.swift
@@ -18,6 +18,7 @@ public protocol ClerkViewFactoryProtocol {
// Inline rendering — returns UIViewController to preserve SwiftUI lifecycle
func createAuthView(mode: String, dismissable: Bool, onEvent: @escaping (String, [String: Any]) -> Void) -> UIViewController?
func createUserProfileView(dismissable: Bool, onEvent: @escaping (String, [String: Any]) -> Void) -> UIViewController?
+ func createOrganizationSwitcherView(hidePersonal: Bool, onEvent: @escaping (String, [String: Any]) -> Void) -> UIViewController?
// SDK operations
func configure(publishableKey: String, bearerToken: String?) async throws
@@ -461,3 +462,85 @@ public class ClerkUserProfileNativeView: UIView {
hostingController?.view.frame = bounds
}
}
+
+// MARK: - Inline View: ClerkOrganizationSwitcherNativeView
+
+public class ClerkOrganizationSwitcherNativeView: UIView {
+ private var hostingController: UIViewController?
+ private var currentHidePersonal: Bool = false
+ private var hasInitialized: Bool = false
+
+ @objc var onOrganizationEvent: RCTBubblingEventBlock?
+
+ @objc var hidePersonal: NSNumber? {
+ didSet {
+ currentHidePersonal = hidePersonal?.boolValue ?? false
+ if hasInitialized { updateView() }
+ }
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override public func didMoveToWindow() {
+ super.didMoveToWindow()
+ if window != nil && !hasInitialized {
+ hasInitialized = true
+ updateView()
+ }
+ }
+
+ private func updateView() {
+ hostingController?.view.removeFromSuperview()
+ hostingController?.removeFromParent()
+ hostingController = nil
+
+ guard let factory = clerkViewFactory else { return }
+
+ guard let returnedController = factory.createOrganizationSwitcherView(
+ hidePersonal: currentHidePersonal,
+ onEvent: { [weak self] eventName, data in
+ let jsonData = (try? JSONSerialization.data(withJSONObject: data)) ?? Data()
+ let jsonString = String(data: jsonData, encoding: .utf8) ?? "{}"
+ self?.onOrganizationEvent?(["type": eventName, "data": jsonString])
+ }
+ ) else { return }
+
+ if let parentVC = findViewController() {
+ parentVC.addChild(returnedController)
+ returnedController.view.frame = bounds
+ returnedController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ returnedController.view.backgroundColor = .clear
+ addSubview(returnedController.view)
+ returnedController.didMove(toParent: parentVC)
+ hostingController = returnedController
+ } else {
+ returnedController.view.frame = bounds
+ returnedController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ returnedController.view.backgroundColor = .clear
+ addSubview(returnedController.view)
+ hostingController = returnedController
+ }
+ }
+
+ private func findViewController() -> UIViewController? {
+ var responder: UIResponder? = self
+ while let nextResponder = responder?.next {
+ if let vc = nextResponder as? UIViewController {
+ return vc
+ }
+ responder = nextResponder
+ }
+ return nil
+ }
+
+ override public func layoutSubviews() {
+ super.layoutSubviews()
+ hostingController?.view.frame = bounds
+ }
+}
diff --git a/packages/expo/ios/ClerkOrganizationSwitcherViewManager.m b/packages/expo/ios/ClerkOrganizationSwitcherViewManager.m
new file mode 100644
index 00000000000..b572aa7bed4
--- /dev/null
+++ b/packages/expo/ios/ClerkOrganizationSwitcherViewManager.m
@@ -0,0 +1,8 @@
+#import
+
+@interface RCT_EXTERN_MODULE(ClerkOrganizationSwitcherViewManager, RCTViewManager)
+
+RCT_EXPORT_VIEW_PROPERTY(hidePersonal, NSNumber)
+RCT_EXPORT_VIEW_PROPERTY(onOrganizationEvent, RCTBubblingEventBlock)
+
+@end
diff --git a/packages/expo/ios/ClerkOrganizationSwitcherViewManager.swift b/packages/expo/ios/ClerkOrganizationSwitcherViewManager.swift
new file mode 100644
index 00000000000..c61b7432b0a
--- /dev/null
+++ b/packages/expo/ios/ClerkOrganizationSwitcherViewManager.swift
@@ -0,0 +1,13 @@
+import React
+
+@objc(ClerkOrganizationSwitcherViewManager)
+class ClerkOrganizationSwitcherViewManager: RCTViewManager {
+
+ override static func requiresMainQueueSetup() -> Bool {
+ return true
+ }
+
+ override func view() -> UIView! {
+ return ClerkOrganizationSwitcherNativeView()
+ }
+}
diff --git a/packages/expo/ios/ClerkViewFactory.swift b/packages/expo/ios/ClerkViewFactory.swift
index 7e1925f41be..407fe6b226e 100644
--- a/packages/expo/ios/ClerkViewFactory.swift
+++ b/packages/expo/ios/ClerkViewFactory.swift
@@ -209,6 +209,20 @@ public final class ClerkViewFactory: ClerkViewFactoryProtocol {
)
}
+ public func createOrganizationSwitcherView(
+ hidePersonal: Bool,
+ onEvent: @escaping (String, [String: Any]) -> Void
+ ) -> UIViewController? {
+ makeHostingController(
+ rootView: ClerkInlineOrganizationSwitcherWrapperView(
+ hidePersonal: hidePersonal,
+ lightTheme: lightTheme,
+ darkTheme: darkTheme,
+ onEvent: onEvent
+ )
+ )
+ }
+
@MainActor
public func getSession() async -> [String: Any]? {
guard Self.clerkConfigured, let session = Clerk.shared.session else {
@@ -647,6 +661,41 @@ struct ClerkInlineAuthWrapperView: View {
}
}
+// MARK: - Inline Organization Switcher Wrapper (for embedded rendering)
+
+struct ClerkInlineOrganizationSwitcherWrapperView: View {
+ let hidePersonal: Bool
+ let lightTheme: ClerkTheme?
+ let darkTheme: ClerkTheme?
+ let onEvent: (String, [String: Any]) -> Void
+
+ @Environment(\.colorScheme) private var colorScheme
+
+ // Observe the active organization id to emit `organizationChanged` events.
+ // The native OrganizationSwitcher view itself does not expose a callback;
+ // it mutates Clerk.shared.organization via setActive internally.
+ @State private var lastOrganizationId: String? = Clerk.shared.organization?.id
+
+ var body: some View {
+ let view = OrganizationSwitcher(hidePersonal: hidePersonal)
+ .environment(Clerk.shared)
+ let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
+ let themedView = Group {
+ if let theme {
+ view.environment(\.clerkTheme, theme)
+ } else {
+ view
+ }
+ }
+ themedView
+ .onChange(of: Clerk.shared.organization?.id) { _, newId in
+ guard newId != lastOrganizationId else { return }
+ lastOrganizationId = newId
+ onEvent("organizationChanged", ["organizationId": newId as Any])
+ }
+ }
+}
+
// MARK: - Inline Profile View Wrapper (for embedded rendering)
struct ClerkInlineProfileWrapperView: View {
diff --git a/packages/expo/src/native/OrganizationSwitcher.tsx b/packages/expo/src/native/OrganizationSwitcher.tsx
new file mode 100644
index 00000000000..ece48a50748
--- /dev/null
+++ b/packages/expo/src/native/OrganizationSwitcher.tsx
@@ -0,0 +1,118 @@
+import { useCallback } from 'react';
+import type { StyleProp, ViewStyle } from 'react-native';
+import { StyleSheet, Text, View } from 'react-native';
+
+import NativeClerkOrganizationSwitcher from '../specs/NativeClerkOrganizationSwitcher';
+import { isNativeSupported } from '../utils/native-module';
+
+export interface OrganizationSwitcherProps {
+ /**
+ * Hide the personal account row inside the switcher.
+ *
+ * iOS only today. Android's prebuilt switcher does not currently expose a
+ * personal-account row; this prop is a no-op there. Once clerk-android's
+ * MOBILE-497 parity work lands, the prop will apply on both platforms.
+ *
+ * @default false
+ */
+ hidePersonal?: boolean;
+
+ /**
+ * Invoked after the active organization changes.
+ *
+ * `organizationId` is the new active organization's id, or `null` when the
+ * user switched to their personal account.
+ */
+ onOrganizationChanged?: (event: { organizationId: string | null }) => void;
+
+ /**
+ * Style applied to the container view.
+ */
+ style?: StyleProp;
+}
+
+/**
+ * A pre-built native control for switching the active organization.
+ *
+ * `OrganizationSwitcher` renders inline within your React Native view hierarchy,
+ * powered by:
+ * - **iOS**: clerk-ios (SwiftUI) — https://github.com/clerk/clerk-ios
+ * - **Android**: clerk-android (Jetpack Compose) — https://github.com/clerk/clerk-android
+ *
+ * The switcher only renders when Organizations are enabled in the Clerk
+ * environment and a user is signed in. Use `useOrganization()` to react to
+ * organization changes in JS.
+ *
+ * @example
+ * ```tsx
+ * import { OrganizationSwitcher } from '@clerk/expo/native';
+ *
+ * export default function Header() {
+ * return ;
+ * }
+ * ```
+ *
+ * @see {@link https://clerk.com/docs/components/organization/organization-switcher} Clerk OrganizationSwitcher Documentation
+ */
+export function OrganizationSwitcher({
+ hidePersonal = false,
+ onOrganizationChanged,
+ style,
+}: OrganizationSwitcherProps) {
+ const handleOrganizationEvent = useCallback(
+ (event: { nativeEvent: { type: string; data: string } }) => {
+ const { type, data: rawData } = event.nativeEvent;
+ if (type !== 'organizationChanged') {
+ return;
+ }
+
+ let parsed: { organizationId?: string | null } = {};
+ try {
+ parsed = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
+ } catch {
+ // Malformed payload — surface a null change so consumers can refresh.
+ }
+
+ onOrganizationChanged?.({ organizationId: parsed.organizationId ?? null });
+ },
+ [onOrganizationChanged],
+ );
+
+ if (!isNativeSupported || !NativeClerkOrganizationSwitcher) {
+ return (
+
+
+ {!isNativeSupported
+ ? 'Native OrganizationSwitcher is only available on iOS and Android'
+ : 'Native OrganizationSwitcher requires the @clerk/expo plugin. Add "@clerk/expo" to your app.json plugins array.'}
+
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ // Without an explicit size, the underlying RN view has zero height — the
+ // SwiftUI / Compose content overflows visually but taps fall through to
+ // the parent. Consumers can override via the `style` prop.
+ nativeDefault: {
+ alignSelf: 'stretch',
+ minHeight: 56,
+ },
+ container: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ text: {
+ fontSize: 14,
+ color: '#666',
+ },
+});
diff --git a/packages/expo/src/native/index.ts b/packages/expo/src/native/index.ts
index 8ccd60b6f2c..09d69b0647a 100644
--- a/packages/expo/src/native/index.ts
+++ b/packages/expo/src/native/index.ts
@@ -22,6 +22,7 @@
* ## Components
*
* - {@link AuthView} - Authentication flow (sign-in/sign-up), renders inline
+ * - {@link OrganizationSwitcher} - Active organization switcher, renders inline
* - {@link UserProfileView} - User profile and account management, renders inline
* - {@link UserButton} - Avatar button that opens native profile modal
*
@@ -30,6 +31,8 @@
export { AuthView } from './AuthView';
export type { AuthViewProps, AuthViewMode } from './AuthView.types';
+export { OrganizationSwitcher } from './OrganizationSwitcher';
+export type { OrganizationSwitcherProps } from './OrganizationSwitcher';
export { UserButton } from './UserButton';
export type { UserButtonProps } from './UserButton';
export { UserProfileView } from './UserProfileView';
diff --git a/packages/expo/src/specs/NativeClerkOrganizationSwitcher.ts b/packages/expo/src/specs/NativeClerkOrganizationSwitcher.ts
new file mode 100644
index 00000000000..1817e22273b
--- /dev/null
+++ b/packages/expo/src/specs/NativeClerkOrganizationSwitcher.ts
@@ -0,0 +1,15 @@
+/* eslint-disable import/namespace, import/default, import/no-named-as-default, import/no-named-as-default-member, simple-import-sort/imports */
+// These deep imports from react-native internals are required by codegen.
+import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
+import type { HostComponent, ViewProps } from 'react-native';
+import type { BubblingEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
+/* eslint-enable import/namespace, import/default, import/no-named-as-default, import/no-named-as-default-member, simple-import-sort/imports */
+
+type OrganizationEvent = Readonly<{ type: string; data: string }>;
+
+interface NativeProps extends ViewProps {
+ hidePersonal?: boolean;
+ onOrganizationEvent?: BubblingEventHandler;
+}
+
+export default codegenNativeComponent('ClerkOrganizationSwitcherView') as HostComponent;