@clerk/expo-native-components 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -2
  3. package/android/build.gradle +152 -0
  4. package/android/src/main/AndroidManifest.xml +4 -0
  5. package/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt +241 -0
  6. package/android/src/main/java/expo/modules/clerk/ClerkComposeNativeViewHost.kt +148 -0
  7. package/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt +1077 -0
  8. package/android/src/main/java/expo/modules/clerk/ClerkUserButtonViewModule.kt +156 -0
  9. package/android/src/main/java/expo/modules/clerk/ClerkUserProfileCustomPageState.kt +120 -0
  10. package/android/src/main/java/expo/modules/clerk/ClerkUserProfileViewModule.kt +315 -0
  11. package/android/src/test/java/expo/modules/clerk/BiometricCredentialBridgeTest.kt +326 -0
  12. package/android/src/test/java/expo/modules/clerk/ClerkUserProfileCustomPageStateTest.kt +163 -0
  13. package/app.plugin.d.ts +5 -0
  14. package/app.plugin.js +333 -0
  15. package/dist/AuthView.d.ts +35 -0
  16. package/dist/AuthView.d.ts.map +1 -0
  17. package/dist/AuthView.js +79 -0
  18. package/dist/AuthView.js.map +1 -0
  19. package/dist/AuthView.types.d.ts +62 -0
  20. package/dist/AuthView.types.d.ts.map +1 -0
  21. package/dist/AuthView.types.js +0 -0
  22. package/dist/EmbeddedNavigation.types.d.ts +24 -0
  23. package/dist/EmbeddedNavigation.types.d.ts.map +1 -0
  24. package/dist/EmbeddedNavigation.types.js +0 -0
  25. package/dist/UserButton.d.ts +38 -0
  26. package/dist/UserButton.d.ts.map +1 -0
  27. package/dist/UserButton.js +68 -0
  28. package/dist/UserButton.js.map +1 -0
  29. package/dist/UserProfileCustomPages.d.ts +81 -0
  30. package/dist/UserProfileCustomPages.d.ts.map +1 -0
  31. package/dist/UserProfileCustomPages.js +140 -0
  32. package/dist/UserProfileCustomPages.js.map +1 -0
  33. package/dist/UserProfileView.d.ts +76 -0
  34. package/dist/UserProfileView.d.ts.map +1 -0
  35. package/dist/UserProfileView.js +101 -0
  36. package/dist/UserProfileView.js.map +1 -0
  37. package/dist/_virtual/_rolldown/runtime.js +29 -0
  38. package/dist/index.d.ts +41 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +37 -0
  41. package/dist/specs/NativeClerkAuthView.android.d.ts +15 -0
  42. package/dist/specs/NativeClerkAuthView.android.d.ts.map +1 -0
  43. package/dist/specs/NativeClerkAuthView.android.js +8 -0
  44. package/dist/specs/NativeClerkAuthView.android.js.map +1 -0
  45. package/dist/specs/NativeClerkAuthView.d.ts +15 -0
  46. package/dist/specs/NativeClerkAuthView.d.ts.map +1 -0
  47. package/dist/specs/NativeClerkAuthView.js +9 -0
  48. package/dist/specs/NativeClerkAuthView.js.map +1 -0
  49. package/dist/specs/NativeClerkModule.d.ts +4 -0
  50. package/dist/specs/NativeClerkModule.d.ts.map +1 -0
  51. package/dist/specs/NativeClerkModule.js +8 -0
  52. package/dist/specs/NativeClerkModule.js.map +1 -0
  53. package/dist/specs/NativeClerkModule.types.d.ts +11 -0
  54. package/dist/specs/NativeClerkModule.types.d.ts.map +1 -0
  55. package/dist/specs/NativeClerkModule.types.js +0 -0
  56. package/dist/specs/NativeClerkModule.web.d.ts +4 -0
  57. package/dist/specs/NativeClerkModule.web.d.ts.map +1 -0
  58. package/dist/specs/NativeClerkModule.web.js +7 -0
  59. package/dist/specs/NativeClerkModule.web.js.map +1 -0
  60. package/dist/specs/NativeClerkUserButtonView.android.d.ts +6 -0
  61. package/dist/specs/NativeClerkUserButtonView.android.d.ts.map +1 -0
  62. package/dist/specs/NativeClerkUserButtonView.android.js +8 -0
  63. package/dist/specs/NativeClerkUserButtonView.android.js.map +1 -0
  64. package/dist/specs/NativeClerkUserButtonView.d.ts +6 -0
  65. package/dist/specs/NativeClerkUserButtonView.d.ts.map +1 -0
  66. package/dist/specs/NativeClerkUserButtonView.js +9 -0
  67. package/dist/specs/NativeClerkUserButtonView.js.map +1 -0
  68. package/dist/specs/NativeClerkUserProfileView.android.d.ts +13 -0
  69. package/dist/specs/NativeClerkUserProfileView.android.d.ts.map +1 -0
  70. package/dist/specs/NativeClerkUserProfileView.android.js +8 -0
  71. package/dist/specs/NativeClerkUserProfileView.android.js.map +1 -0
  72. package/dist/specs/NativeClerkUserProfileView.d.ts +13 -0
  73. package/dist/specs/NativeClerkUserProfileView.d.ts.map +1 -0
  74. package/dist/specs/NativeClerkUserProfileView.js +9 -0
  75. package/dist/specs/NativeClerkUserProfileView.js.map +1 -0
  76. package/dist/useAuthViewState.d.ts +12 -0
  77. package/dist/useAuthViewState.d.ts.map +1 -0
  78. package/dist/useAuthViewState.js +76 -0
  79. package/dist/useAuthViewState.js.map +1 -0
  80. package/dist/utils/native-module.d.ts +3 -0
  81. package/dist/utils/native-module.d.ts.map +1 -0
  82. package/dist/utils/native-module.js +14 -0
  83. package/dist/utils/native-module.js.map +1 -0
  84. package/expo-module.config.json +15 -0
  85. package/ios/ClerkAppDelegateSubscriber.swift +16 -0
  86. package/ios/ClerkAuthNativeView.swift +157 -0
  87. package/ios/ClerkExpo.podspec +67 -0
  88. package/ios/ClerkExpoModule.swift +315 -0
  89. package/ios/ClerkNativeBridge.swift +1575 -0
  90. package/ios/ClerkNativeViewHost.swift +204 -0
  91. package/ios/ClerkUserButtonNativeView.swift +34 -0
  92. package/ios/ClerkUserProfileNativeView.swift +92 -0
  93. package/ios/Tests/ClerkAuthNativeViewPaperTests.swift +30 -0
  94. package/ios/Tests/ClerkNativeBridgeTests.swift +185 -0
  95. package/ios/Tests/ClerkNativeViewHostTests.swift +66 -0
  96. package/ios/Tests/ClerkUserProfileCustomPageStateTests.swift +311 -0
  97. package/package.json +79 -3
  98. package/react-native.config.js +8 -0
  99. package/src/specs/NativeClerkAuthView.android.ts +15 -0
  100. package/src/specs/NativeClerkAuthView.ts +19 -0
  101. package/src/specs/NativeClerkModule.ts +6 -0
  102. package/src/specs/NativeClerkModule.types.ts +9 -0
  103. package/src/specs/NativeClerkModule.web.ts +3 -0
  104. package/src/specs/NativeClerkUserButtonView.android.ts +9 -0
  105. package/src/specs/NativeClerkUserButtonView.ts +11 -0
  106. package/src/specs/NativeClerkUserProfileView.android.ts +13 -0
  107. package/src/specs/NativeClerkUserProfileView.ts +17 -0
  108. package/src/specs/__tests__/native-view.web.test.ts +43 -0
package/app.plugin.js ADDED
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Expo config plugin for @clerk/expo-native-components
3
+ * Configures iOS and Android for the Clerk native SDKs (clerk-ios / clerk-android)
4
+ *
5
+ * When this plugin is used:
6
+ * 1. iOS is configured with the required deployment target and metadata
7
+ * 2. Android is configured with packaging exclusions for dependencies
8
+ *
9
+ * Native modules and views are registered via Expo Modules autolinking.
10
+ */
11
+ const {
12
+ createRunOncePlugin,
13
+ withXcodeProject,
14
+ withDangerousMod,
15
+ withInfoPlist,
16
+ withAppBuildGradle,
17
+ withGradleProperties,
18
+ } = require('@expo/config-plugins');
19
+ const path = require('path');
20
+ const fs = require('fs');
21
+ const packageJson = require('./package.json');
22
+
23
+ const CLERK_MIN_IOS_VERSION = '17.0';
24
+ const HOST_SDK_VERSION_GRADLE_PROPERTY = 'clerkExpo.hostSdkVersion';
25
+
26
+ const resolveClerkExpo = projectRoot => {
27
+ try {
28
+ const paths = [projectRoot, process.cwd()].filter(Boolean);
29
+ const clerkExpoPackageJsonPath = require.resolve('@clerk/expo/package.json', { paths });
30
+ return {
31
+ dir: path.dirname(clerkExpoPackageJsonPath),
32
+ version: require(clerkExpoPackageJsonPath).version,
33
+ };
34
+ } catch {
35
+ return null;
36
+ }
37
+ };
38
+
39
+ const withClerkIOS = (config, hostSdkVersion) => {
40
+ console.log('✅ Clerk iOS plugin loaded');
41
+
42
+ // IMPORTANT: Set iOS deployment target in Podfile.properties.json BEFORE pod install
43
+ // This ensures ClerkExpo pod gets installed (it requires iOS 17.0)
44
+ config = withDangerousMod(config, [
45
+ 'ios',
46
+ async config => {
47
+ const podfilePropertiesPath = path.join(config.modRequest.platformProjectRoot, 'Podfile.properties.json');
48
+
49
+ let properties = {};
50
+ if (fs.existsSync(podfilePropertiesPath)) {
51
+ try {
52
+ properties = JSON.parse(fs.readFileSync(podfilePropertiesPath, 'utf8'));
53
+ } catch {
54
+ // If file exists but is invalid JSON, start fresh
55
+ }
56
+ }
57
+
58
+ // Set the iOS deployment target
59
+ if (
60
+ !properties['ios.deploymentTarget'] ||
61
+ parseFloat(properties['ios.deploymentTarget']) < parseFloat(CLERK_MIN_IOS_VERSION)
62
+ ) {
63
+ properties['ios.deploymentTarget'] = CLERK_MIN_IOS_VERSION;
64
+ fs.writeFileSync(podfilePropertiesPath, JSON.stringify(properties, null, 2) + '\n');
65
+ console.log(`✅ Set ios.deploymentTarget to ${CLERK_MIN_IOS_VERSION} in Podfile.properties.json`);
66
+ }
67
+
68
+ return config;
69
+ },
70
+ ]);
71
+
72
+ // First update the iOS deployment target to 17.0 (required by Clerk iOS SDK)
73
+ config = withXcodeProject(config, config => {
74
+ const xcodeProject = config.modResults;
75
+
76
+ try {
77
+ // Update deployment target in all build configurations
78
+ const buildConfigs = xcodeProject.hash.project.objects.XCBuildConfiguration || {};
79
+
80
+ for (const [uuid, buildConfig] of Object.entries(buildConfigs)) {
81
+ if (buildConfig && buildConfig.buildSettings) {
82
+ const currentTarget = buildConfig.buildSettings.IPHONEOS_DEPLOYMENT_TARGET;
83
+ if (currentTarget && parseFloat(currentTarget) < parseFloat(CLERK_MIN_IOS_VERSION)) {
84
+ buildConfig.buildSettings.IPHONEOS_DEPLOYMENT_TARGET = CLERK_MIN_IOS_VERSION;
85
+ }
86
+ }
87
+ }
88
+
89
+ console.log(`✅ Updated iOS deployment target to ${CLERK_MIN_IOS_VERSION}`);
90
+ } catch (error) {
91
+ console.error('❌ Error updating deployment target:', error.message);
92
+ }
93
+
94
+ return config;
95
+ });
96
+
97
+ config = withInfoPlist(config, modConfig => {
98
+ modConfig.modResults.ClerkExpoVersion = hostSdkVersion;
99
+ return modConfig;
100
+ });
101
+
102
+ return config;
103
+ };
104
+
105
+ /**
106
+ * Add packaging exclusions to Android app build.gradle to resolve
107
+ * duplicate META-INF file conflicts from clerk-android dependencies.
108
+ */
109
+ const withClerkAndroid = (config, hostSdkVersion) => {
110
+ console.log('✅ Clerk Android plugin loaded');
111
+
112
+ config = withGradleProperties(config, modConfig => {
113
+ modConfig.modResults = modConfig.modResults.filter(
114
+ item => !(item.type === 'property' && item.key === HOST_SDK_VERSION_GRADLE_PROPERTY),
115
+ );
116
+ modConfig.modResults.push({ type: 'property', key: HOST_SDK_VERSION_GRADLE_PROPERTY, value: hostSdkVersion });
117
+ return modConfig;
118
+ });
119
+
120
+ return withAppBuildGradle(config, modConfig => {
121
+ let buildGradle = modConfig.modResults.contents;
122
+
123
+ // --- META-INF exclusion ---
124
+ if (!buildGradle.includes('META-INF/versions/9/OSGI-INF/MANIFEST.MF')) {
125
+ // AGP 8+ uses `packaging` DSL, older versions use `packagingOptions`
126
+ const packagingMatch = buildGradle.match(/packaging\s*\{/) || buildGradle.match(/packagingOptions\s*\{/);
127
+ if (packagingMatch) {
128
+ const blockName = packagingMatch[0].trim().replace(/\s*\{$/, '');
129
+ const resourcesExclude = `${blockName} {
130
+ // Clerk Android SDK: exclude duplicate META-INF files
131
+ resources {
132
+ excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF']
133
+ }`;
134
+
135
+ buildGradle = buildGradle.replace(new RegExp(`${blockName}\\s*\\{`), resourcesExclude);
136
+ } else {
137
+ // No packaging block found; append one at the end of the android block
138
+ const androidBlockEnd = buildGradle.lastIndexOf('}');
139
+ if (androidBlockEnd !== -1) {
140
+ const packagingBlock = `\n packaging {\n resources {\n excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF']\n }\n }\n`;
141
+ buildGradle = buildGradle.slice(0, androidBlockEnd) + packagingBlock + buildGradle.slice(androidBlockEnd);
142
+ }
143
+ }
144
+ console.log('✅ Clerk Android packaging exclusions added');
145
+ }
146
+
147
+ // --- Kotlin metadata version check skip ---
148
+ if (!buildGradle.includes('-Xskip-metadata-version-check')) {
149
+ const kotlinOptionsMatch = buildGradle.match(/kotlinOptions\s*\{/);
150
+ if (kotlinOptionsMatch) {
151
+ buildGradle = buildGradle.replace(
152
+ /kotlinOptions\s*\{/,
153
+ `kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']`,
154
+ );
155
+ } else {
156
+ const androidMatch = buildGradle.match(/android\s*\{/);
157
+ if (androidMatch) {
158
+ buildGradle = buildGradle.replace(
159
+ /android\s*\{/,
160
+ `android {\n kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']\n }`,
161
+ );
162
+ }
163
+ }
164
+ console.log('✅ Clerk Android Kotlin metadata version check skip added');
165
+ }
166
+
167
+ modConfig.modResults.contents = buildGradle;
168
+ return modConfig;
169
+ });
170
+ };
171
+
172
+ /**
173
+ * Write ClerkKeychainService to Info.plist when keychainService is provided.
174
+ * This allows extension apps (watch, widget, app clip) to share the same
175
+ * keychain entry as the main app by using a custom service identifier.
176
+ */
177
+ const withClerkKeychainService = (config, { keychainService } = {}) => {
178
+ if (!keychainService) {
179
+ return config;
180
+ }
181
+
182
+ return withInfoPlist(config, modConfig => {
183
+ modConfig.modResults.ClerkKeychainService = keychainService;
184
+ console.log(`✅ Set ClerkKeychainService in Info.plist: ${keychainService}`);
185
+ return modConfig;
186
+ });
187
+ };
188
+
189
+ /**
190
+ * Apply a custom theme to Clerk native components (iOS + Android).
191
+ *
192
+ * Accepts a `theme` prop pointing to a JSON file with optional keys:
193
+ * - colors: { primary, background, input, danger, success, warning,
194
+ * foreground, mutedForeground, primaryForeground, inputForeground,
195
+ * neutral, border, ring, muted, shadow, secondaryButtonBackground,
196
+ * secondaryButtonForeground } (hex color strings)
197
+ * - darkColors: same keys as colors (for dark mode)
198
+ * - design: { fontFamily: string, borderRadius: number }
199
+ *
200
+ * iOS: Embeds the parsed JSON into Info.plist under key "ClerkTheme".
201
+ * Android: Copies the JSON file to android/app/src/main/assets/clerk_theme.json.
202
+ */
203
+ const VALID_COLOR_KEYS = [
204
+ 'primary',
205
+ 'background',
206
+ 'input',
207
+ 'danger',
208
+ 'success',
209
+ 'warning',
210
+ 'foreground',
211
+ 'mutedForeground',
212
+ 'primaryForeground',
213
+ 'inputForeground',
214
+ 'neutral',
215
+ 'border',
216
+ 'ring',
217
+ 'muted',
218
+ 'shadow',
219
+ 'secondaryButtonBackground',
220
+ 'secondaryButtonForeground',
221
+ ];
222
+
223
+ const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
224
+
225
+ function isPlainObject(value) {
226
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
227
+ }
228
+
229
+ function validateThemeJson(theme) {
230
+ if (!isPlainObject(theme)) {
231
+ throw new Error('Clerk theme: theme JSON must be a plain object');
232
+ }
233
+
234
+ const validateColors = (colors, label) => {
235
+ if (!isPlainObject(colors)) {
236
+ throw new Error(`Clerk theme: ${label} must be an object`);
237
+ }
238
+ for (const [key, value] of Object.entries(colors)) {
239
+ if (!VALID_COLOR_KEYS.includes(key)) {
240
+ console.warn(`⚠️ Clerk theme: unknown color key "${key}" in ${label}, ignoring`);
241
+ continue;
242
+ }
243
+ if (typeof value !== 'string' || !HEX_COLOR_REGEX.test(value)) {
244
+ throw new Error(`Clerk theme: invalid hex color for ${label}.${key}: "${value}"`);
245
+ }
246
+ }
247
+ };
248
+
249
+ if (theme.colors != null) validateColors(theme.colors, 'colors');
250
+ if (theme.darkColors != null) validateColors(theme.darkColors, 'darkColors');
251
+
252
+ if (theme.design != null) {
253
+ if (!isPlainObject(theme.design)) {
254
+ throw new Error(`Clerk theme: design must be an object`);
255
+ }
256
+ if (theme.design.fontFamily != null && typeof theme.design.fontFamily !== 'string') {
257
+ throw new Error(`Clerk theme: design.fontFamily must be a string`);
258
+ }
259
+ if (theme.design.borderRadius != null && typeof theme.design.borderRadius !== 'number') {
260
+ throw new Error(`Clerk theme: design.borderRadius must be a number`);
261
+ }
262
+ }
263
+ }
264
+
265
+ const withClerkTheme = (config, props = {}) => {
266
+ const { theme } = props;
267
+ if (!theme) return config;
268
+
269
+ // Resolve the theme file path relative to the project root
270
+ const themePath = path.resolve(theme);
271
+ if (!fs.existsSync(themePath)) {
272
+ console.warn(`⚠️ Clerk theme file not found: ${themePath}, skipping theme`);
273
+ return config;
274
+ }
275
+
276
+ let themeJson;
277
+ try {
278
+ themeJson = JSON.parse(fs.readFileSync(themePath, 'utf8'));
279
+ validateThemeJson(themeJson);
280
+ } catch (e) {
281
+ throw new Error(`Clerk theme: failed to parse ${themePath}: ${e.message}`);
282
+ }
283
+
284
+ // iOS: Embed theme in Info.plist under "ClerkTheme"
285
+ config = withInfoPlist(config, modConfig => {
286
+ modConfig.modResults.ClerkTheme = themeJson;
287
+ console.log('✅ Embedded Clerk theme in Info.plist');
288
+ return modConfig;
289
+ });
290
+
291
+ // Android: Copy theme JSON to assets
292
+ config = withDangerousMod(config, [
293
+ 'android',
294
+ async config => {
295
+ const assetsDir = path.join(config.modRequest.platformProjectRoot, 'app', 'src', 'main', 'assets');
296
+ if (!fs.existsSync(assetsDir)) {
297
+ fs.mkdirSync(assetsDir, { recursive: true });
298
+ }
299
+ const destPath = path.join(assetsDir, 'clerk_theme.json');
300
+ fs.writeFileSync(destPath, JSON.stringify(themeJson, null, 2) + '\n');
301
+ console.log('✅ Copied Clerk theme to Android assets');
302
+ return config;
303
+ },
304
+ ]);
305
+
306
+ return config;
307
+ };
308
+
309
+ const withClerkExpoNativeComponents = (config, props = {}, resolve = resolveClerkExpo) => {
310
+ const clerkExpo = resolve(config._internal?.projectRoot);
311
+ if (clerkExpo && fs.existsSync(path.join(clerkExpo.dir, 'expo-module.config.json'))) {
312
+ throw new Error(
313
+ `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.`,
314
+ );
315
+ }
316
+ // Native requests report the @clerk/expo version in the x-clerk-host-sdk-version header.
317
+ const hostSdkVersion = clerkExpo?.version ?? packageJson.version;
318
+
319
+ config = withClerkIOS(config, hostSdkVersion);
320
+ config = withClerkAndroid(config, hostSdkVersion);
321
+ config = withClerkKeychainService(config, props);
322
+ config = withClerkTheme(config, props);
323
+ return config;
324
+ };
325
+
326
+ module.exports = createRunOncePlugin(withClerkExpoNativeComponents, packageJson.name, packageJson.version);
327
+ module.exports._testing = {
328
+ withClerkExpoNativeComponents,
329
+ validateThemeJson,
330
+ isPlainObject,
331
+ VALID_COLOR_KEYS,
332
+ HEX_COLOR_REGEX,
333
+ };
@@ -0,0 +1,35 @@
1
+ import type { AuthViewProps } from './AuthView.types';
2
+ /**
3
+ * A pre-built native authentication component that handles sign-in and sign-up flows.
4
+ *
5
+ * `AuthView` renders inline within your React Native view hierarchy, powered by:
6
+ * - **iOS**: clerk-ios (SwiftUI) - https://github.com/clerk/clerk-ios
7
+ * - **Android**: clerk-android (Jetpack Compose) - https://github.com/clerk/clerk-android
8
+ *
9
+ * After authentication completes, the session is automatically synced with the JS SDK.
10
+ * Use `useAuth()`, `useUser()`, or `useSession()` to react to authentication
11
+ * state changes.
12
+ *
13
+ * To push the auth flow onto your own navigation stack, hide the route's header and
14
+ * pass `onHostBack` so Clerk's own chrome takes over.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { AuthView } from '@clerk/expo-native-components';
19
+ * import { useAuth } from '@clerk/expo';
20
+ *
21
+ * export default function SignInScreen() {
22
+ * const { isSignedIn } = useAuth();
23
+ *
24
+ * useEffect(() => {
25
+ * if (isSignedIn) router.replace('/home');
26
+ * }, [isSignedIn]);
27
+ *
28
+ * return <AuthView />;
29
+ * }
30
+ * ```
31
+ *
32
+ * @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
33
+ */
34
+ export declare function AuthView({ logo, mode, isDismissible, logoMaxHeight, onDismiss, onHostBack, }: AuthViewProps): import("react/jsx-runtime").JSX.Element;
35
+ //# sourceMappingURL=AuthView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthView.d.ts","sourceRoot":"","sources":["../src/AuthView.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,IAAmB,EACnB,aAAoB,EACpB,aAAa,EACb,SAAS,EACT,UAAU,GACX,EAAE,aAAa,2CA0Cf"}
@@ -0,0 +1,79 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('./_virtual/_rolldown/runtime.js');
3
+ let react = require("react");
4
+ let react_native = require("react-native");
5
+ let src_specs_NativeClerkAuthView = require("./specs/NativeClerkAuthView");
6
+ src_specs_NativeClerkAuthView = require_runtime.__toESM(src_specs_NativeClerkAuthView);
7
+ let src_utils_native_module = require("./utils/native-module");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+
10
+ //#region src/AuthView.tsx
11
+ /**
12
+ * A pre-built native authentication component that handles sign-in and sign-up flows.
13
+ *
14
+ * `AuthView` renders inline within your React Native view hierarchy, powered by:
15
+ * - **iOS**: clerk-ios (SwiftUI) - https://github.com/clerk/clerk-ios
16
+ * - **Android**: clerk-android (Jetpack Compose) - https://github.com/clerk/clerk-android
17
+ *
18
+ * After authentication completes, the session is automatically synced with the JS SDK.
19
+ * Use `useAuth()`, `useUser()`, or `useSession()` to react to authentication
20
+ * state changes.
21
+ *
22
+ * To push the auth flow onto your own navigation stack, hide the route's header and
23
+ * pass `onHostBack` so Clerk's own chrome takes over.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * import { AuthView } from '@clerk/expo-native-components';
28
+ * import { useAuth } from '@clerk/expo';
29
+ *
30
+ * export default function SignInScreen() {
31
+ * const { isSignedIn } = useAuth();
32
+ *
33
+ * useEffect(() => {
34
+ * if (isSignedIn) router.replace('/home');
35
+ * }, [isSignedIn]);
36
+ *
37
+ * return <AuthView />;
38
+ * }
39
+ * ```
40
+ *
41
+ * @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
42
+ */
43
+ function AuthView({ logo, mode = "signInOrUp", isDismissible = true, logoMaxHeight, onDismiss, onHostBack }) {
44
+ const handleAuthEvent = (0, react.useCallback)((event) => {
45
+ if (event.nativeEvent.type === "dismissed") onDismiss?.();
46
+ }, [onDismiss]);
47
+ if (!src_utils_native_module.isNativeSupported || !src_specs_NativeClerkAuthView.default) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
48
+ style: {
49
+ flex: 1,
50
+ justifyContent: "center",
51
+ alignItems: "center"
52
+ },
53
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
54
+ style: {
55
+ fontSize: 16,
56
+ color: "#666"
57
+ },
58
+ children: !src_utils_native_module.isNativeSupported ? "Native AuthView is only available on iOS and Android" : "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."
59
+ })
60
+ });
61
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(src_specs_NativeClerkAuthView.default, {
62
+ style: { flex: 1 },
63
+ mode,
64
+ isDismissible,
65
+ logoMaxHeight,
66
+ hostBackButton: !!onHostBack,
67
+ onAuthEvent: handleAuthEvent,
68
+ onHostBack: onHostBack ? () => onHostBack() : void 0,
69
+ children: logo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
70
+ collapsable: false,
71
+ style: { alignSelf: "flex-start" },
72
+ children: logo
73
+ }) : null
74
+ });
75
+ }
76
+
77
+ //#endregion
78
+ exports.AuthView = AuthView;
79
+ //# sourceMappingURL=AuthView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthView.js","names":["isNativeSupported","NativeClerkAuthView","View","Text"],"sources":["../src/AuthView.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport type { NativeSyntheticEvent } from 'react-native';\nimport { Text, View } from 'react-native';\n\nimport type { AuthViewProps } from './AuthView.types';\nimport NativeClerkAuthView from './specs/NativeClerkAuthView';\nimport { isNativeSupported } from './utils/native-module';\n\ntype AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;\n\n/**\n * A pre-built native authentication component that handles sign-in and sign-up flows.\n *\n * `AuthView` renders inline within your React Native view hierarchy, powered by:\n * - **iOS**: clerk-ios (SwiftUI) - https://github.com/clerk/clerk-ios\n * - **Android**: clerk-android (Jetpack Compose) - https://github.com/clerk/clerk-android\n *\n * After authentication completes, the session is automatically synced with the JS SDK.\n * Use `useAuth()`, `useUser()`, or `useSession()` to react to authentication\n * state changes.\n *\n * To push the auth flow onto your own navigation stack, hide the route's header and\n * pass `onHostBack` so Clerk's own chrome takes over.\n *\n * @example\n * ```tsx\n * import { AuthView } from '@clerk/expo-native-components';\n * import { useAuth } from '@clerk/expo';\n *\n * export default function SignInScreen() {\n * const { isSignedIn } = useAuth();\n *\n * useEffect(() => {\n * if (isSignedIn) router.replace('/home');\n * }, [isSignedIn]);\n *\n * return <AuthView />;\n * }\n * ```\n *\n * @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation\n */\nexport function AuthView({\n logo,\n mode = 'signInOrUp',\n isDismissible = true,\n logoMaxHeight,\n onDismiss,\n onHostBack,\n}: AuthViewProps) {\n const handleAuthEvent = useCallback(\n (event: AuthNativeEvent) => {\n if (event.nativeEvent.type === 'dismissed') {\n onDismiss?.();\n }\n },\n [onDismiss],\n );\n\n if (!isNativeSupported || !NativeClerkAuthView) {\n return (\n <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>\n <Text style={{ fontSize: 16, color: '#666' }}>\n {!isNativeSupported\n ? 'Native AuthView is only available on iOS and Android'\n : '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.'}\n </Text>\n </View>\n );\n }\n\n return (\n <NativeClerkAuthView\n style={{ flex: 1 }}\n mode={mode}\n isDismissible={isDismissible}\n logoMaxHeight={logoMaxHeight}\n hostBackButton={!!onHostBack}\n onAuthEvent={handleAuthEvent}\n onHostBack={onHostBack ? () => onHostBack() : undefined}\n >\n {logo ? (\n <View\n collapsable={false}\n style={{ alignSelf: 'flex-start' }}\n >\n {logo}\n </View>\n ) : null}\n </NativeClerkAuthView>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,SAAS,EACvB,MACA,OAAO,cACP,gBAAgB,MAChB,eACA,WACA,cACgB;CAChB,MAAM,0CACH,UAA2B;EAC1B,IAAI,MAAM,YAAY,SAAS,aAC7B,YAAY;CAEhB,GACA,CAAC,SAAS,CACZ;CAEA,IAAI,CAACA,6CAAqB,CAACC,uCACzB,OACE,2CAACC,mBAAD;EAAM,OAAO;GAAE,MAAM;GAAG,gBAAgB;GAAU,YAAY;EAAS;YACrE,2CAACC,mBAAD;GAAM,OAAO;IAAE,UAAU;IAAI,OAAO;GAAO;aACxC,CAACH,4CACE,yDACA;EACA;CACF;CAIV,OACE,2CAACC,uCAAD;EACE,OAAO,EAAE,MAAM,EAAE;EACX;EACS;EACA;EACf,gBAAgB,CAAC,CAAC;EAClB,aAAa;EACb,YAAY,mBAAmB,WAAW,IAAI;YAE7C,OACC,2CAACC,mBAAD;GACE,aAAa;GACb,OAAO,EAAE,WAAW,aAAa;aAEhC;EACG,KACJ;CACe;AAEzB"}
@@ -0,0 +1,62 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { EmbeddedNavigationProps } from './EmbeddedNavigation.types';
3
+ /**
4
+ * Authentication mode that determines which flows are available to the user.
5
+ *
6
+ * - `'signInOrUp'` - Allows users to choose between signing in or creating a new account (default)
7
+ * - `'signIn'` - Restricts to sign-in flows only
8
+ * - `'signUp'` - Restricts to sign-up flows only
9
+ */
10
+ export type AuthViewMode = 'signIn' | 'signUp' | 'signInOrUp';
11
+ /**
12
+ * Props for the AuthView component.
13
+ *
14
+ * AuthView renders a native authentication UI inline (fills parent container).
15
+ * Use `useAuth()`, `useUser()`, or `useSession()` to react to authentication
16
+ * state changes.
17
+ */
18
+ export interface AuthViewProps extends EmbeddedNavigationProps {
19
+ /**
20
+ * Replaces the dashboard-configured logo with custom React Native content.
21
+ *
22
+ * The native authentication UI does not apply sizing, spacing, or accessibility
23
+ * attributes to this content. The provided element must define its own layout
24
+ * and accessibility behavior.
25
+ */
26
+ logo?: ReactElement;
27
+ /**
28
+ * Authentication mode that determines which flows are available.
29
+ *
30
+ * - `'signInOrUp'` - Users can choose between signing in or creating an account
31
+ * - `'signIn'` - Only sign-in flows are available
32
+ * - `'signUp'` - Only sign-up flows are available
33
+ *
34
+ * @default 'signInOrUp'
35
+ */
36
+ mode?: AuthViewMode;
37
+ /**
38
+ * Whether the authentication view can be dismissed by the user.
39
+ *
40
+ * When `true`, a dismiss button appears in the navigation bar.
41
+ *
42
+ * When `false`, the user must complete authentication to close the view.
43
+ * Use this for flows where authentication is required to proceed.
44
+ *
45
+ * @default true
46
+ */
47
+ isDismissible?: boolean;
48
+ /**
49
+ * Maximum height of the Clerk application logo, in density-independent pixels.
50
+ *
51
+ * @default 44
52
+ */
53
+ logoMaxHeight?: number;
54
+ /**
55
+ * Called when the native authentication view requests dismissal.
56
+ *
57
+ * This fires when the user dismisses the view, or when the native auth flow
58
+ * finishes and the app-owned presentation should close.
59
+ */
60
+ onDismiss?: () => void;
61
+ }
62
+ //# sourceMappingURL=AuthView.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthView.types.d.ts","sourceRoot":"","sources":["../src/AuthView.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,WAAW,aAAc,SAAQ,uBAAuB;IAC5D;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IAEpB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IAEpB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB"}
File without changes
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Props shared by native components that can be embedded in the host app's own
3
+ * navigation (`UserProfileView`, `AuthView`).
4
+ */
5
+ export interface EmbeddedNavigationProps {
6
+ /**
7
+ * Shows a back button on the component's root screen and calls this when it
8
+ * is tapped. Pop your own route in response.
9
+ *
10
+ * The component keeps its own navigation chrome, so screen titles, back
11
+ * buttons, swipe-back, and transitions inside the component stay native.
12
+ * Use this when the component fills a route whose own header is hidden:
13
+ *
14
+ * ```tsx
15
+ * <Stack.Screen options={{ headerShown: false }} />
16
+ * <UserProfileView isDismissible={false} onHostBack={() => router.back()} />
17
+ * ```
18
+ *
19
+ * The component never leaves the route on its own, so react to auth state
20
+ * for flow completion — swap the content in place, or pop the route.
21
+ */
22
+ onHostBack?: () => void;
23
+ }
24
+ //# sourceMappingURL=EmbeddedNavigation.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbeddedNavigation.types.d.ts","sourceRoot":"","sources":["../src/EmbeddedNavigation.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB"}
File without changes
@@ -0,0 +1,38 @@
1
+ import type { JSX } from 'react';
2
+ import type { UserProfileCustomDestination, UserProfileCustomPage } from './UserProfileCustomPages';
3
+ export interface UserButtonUserProfileProps {
4
+ /** Custom pages displayed as rows in the user profile. */
5
+ customPages?: UserProfileCustomPage[];
6
+ /** Custom destinations that can be pushed from a custom page without creating a profile row. */
7
+ customDestinations?: UserProfileCustomDestination[];
8
+ }
9
+ export interface UserButtonProps {
10
+ /** Configuration passed to the user profile opened by the button. */
11
+ userProfileProps?: UserButtonUserProfileProps;
12
+ }
13
+ /**
14
+ * A pre-built button component that displays the user's avatar.
15
+ *
16
+ * `UserButton` renders the platform-native Clerk user button. Tapping it opens
17
+ * the native user profile surface, matching Clerk's iOS and Android SDKs.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { UserButton } from '@clerk/expo-native-components';
22
+ *
23
+ * export default function Home() {
24
+ * return (
25
+ * <UserButton
26
+ * userProfileProps={{
27
+ * customPages: [{ path: 'api-keys', label: 'API keys', icon: 'key', content: <APIKeysView /> }],
28
+ * }}
29
+ * />
30
+ * );
31
+ * }
32
+ * ```
33
+ *
34
+ * @see {@link UserProfileView} The profile view to render in your own presentation surface
35
+ * @see {@link https://clerk.com/docs/components/user/user-button} Clerk UserButton Documentation
36
+ */
37
+ export declare function UserButton({ userProfileProps }: UserButtonProps): JSX.Element | null;
38
+ //# sourceMappingURL=UserButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserButton.d.ts","sourceRoot":"","sources":["../src/UserButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiC,GAAG,EAAO,MAAM,OAAO,CAAC;AAMrE,OAAO,KAAK,EAEV,4BAA4B,EAC5B,qBAAqB,EAEtB,MAAM,0BAA0B,CAAC;AAiBlC,MAAM,WAAW,0BAA0B;IACzC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAEtC,gGAAgG;IAChG,kBAAkB,CAAC,EAAE,4BAA4B,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;CAC/C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,EAAE,gBAAgB,EAAE,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CA8BpF"}
@@ -0,0 +1,68 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('./_virtual/_rolldown/runtime.js');
3
+ let react = require("react");
4
+ let react_native = require("react-native");
5
+ let src_utils_native_module = require("./utils/native-module");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+ let src_specs_NativeClerkUserButtonView = require("./specs/NativeClerkUserButtonView");
8
+ src_specs_NativeClerkUserButtonView = require_runtime.__toESM(src_specs_NativeClerkUserButtonView);
9
+ let src_UserProfileCustomPages = require("./UserProfileCustomPages");
10
+
11
+ //#region src/UserButton.tsx
12
+ const CustomizableNativeClerkUserButtonView = src_specs_NativeClerkUserButtonView.default;
13
+ /**
14
+ * A pre-built button component that displays the user's avatar.
15
+ *
16
+ * `UserButton` renders the platform-native Clerk user button. Tapping it opens
17
+ * the native user profile surface, matching Clerk's iOS and Android SDKs.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { UserButton } from '@clerk/expo-native-components';
22
+ *
23
+ * export default function Home() {
24
+ * return (
25
+ * <UserButton
26
+ * userProfileProps={{
27
+ * customPages: [{ path: 'api-keys', label: 'API keys', icon: 'key', content: <APIKeysView /> }],
28
+ * }}
29
+ * />
30
+ * );
31
+ * }
32
+ * ```
33
+ *
34
+ * @see {@link UserProfileView} The profile view to render in your own presentation surface
35
+ * @see {@link https://clerk.com/docs/components/user/user-button} Clerk UserButton Documentation
36
+ */
37
+ function UserButton({ userProfileProps }) {
38
+ const nativeViewRef = (0, react.useRef)(null);
39
+ const customPages = userProfileProps?.customPages;
40
+ const customDestinations = userProfileProps?.customDestinations;
41
+ const customRoutes = (0, react.useMemo)(() => [...customPages ?? [], ...customDestinations ?? []], [customDestinations, customPages]);
42
+ const { navigation, presentedPaths, onCustomPageEvent } = (0, src_UserProfileCustomPages.useUserProfileCustomPages)(customRoutes, nativeViewRef);
43
+ const { width, height } = (0, react_native.useWindowDimensions)();
44
+ if (!src_utils_native_module.isNativeSupported || !CustomizableNativeClerkUserButtonView) return null;
45
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomizableNativeClerkUserButtonView, {
46
+ ref: nativeViewRef,
47
+ style: styles.host,
48
+ customPages: (0, src_UserProfileCustomPages.serializeUserProfileCustomPages)(customPages ?? [], customDestinations ?? []),
49
+ onCustomPageEvent,
50
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(src_UserProfileCustomPages.UserProfileCustomPageHosts, {
51
+ customRoutes,
52
+ presentedPaths,
53
+ navigation,
54
+ style: {
55
+ width,
56
+ height
57
+ }
58
+ })
59
+ });
60
+ }
61
+ const styles = react_native.StyleSheet.create({ host: {
62
+ width: 36,
63
+ height: 36
64
+ } });
65
+
66
+ //#endregion
67
+ exports.UserButton = UserButton;
68
+ //# sourceMappingURL=UserButton.js.map