diff --git a/.changeset/fix-query-client-rn.md b/.changeset/fix-query-client-rn.md new file mode 100644 index 00000000000..2ac86035d4a --- /dev/null +++ b/.changeset/fix-query-client-rn.md @@ -0,0 +1,5 @@ +--- +"@clerk/expo": patch +--- + +Fix `useOrganizationList` and other query-based hooks returning empty data on React Native by synchronously providing a `QueryClient` instance diff --git a/packages/expo/src/provider/singleton/createClerkInstance.ts b/packages/expo/src/provider/singleton/createClerkInstance.ts index 2a361bad54a..c4bfe5dc030 100644 --- a/packages/expo/src/provider/singleton/createClerkInstance.ts +++ b/packages/expo/src/provider/singleton/createClerkInstance.ts @@ -112,6 +112,26 @@ export function createClerkInstance(ClerkClass: typeof Clerk) { __internal_clerkOptions = { publishableKey, proxyUrl, domain }; __internal_clerk = new ClerkClass(publishableKey, { proxyUrl, domain }) as unknown as BrowserClerk; + // The clerk-js native bundle uses rspack code-splitting for the internal QueryClient. + // On React Native, rspack's chunk loading doesn't work (Metro bundles into a single file), + // so the dynamic import never resolves and __internal_queryClient stays undefined. + // This breaks hooks that depend on the query client (useOrganizationList, etc.). + // Override the getter to synchronously create a QueryClient on first access. + { + // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports + const { QueryClient } = require('@tanstack/query-core'); + let queryClient: InstanceType | undefined; + Object.defineProperty(__internal_clerk, '__internal_queryClient', { + get() { + if (!queryClient) { + queryClient = new QueryClient(); + } + return { __tag: 'clerk-rq-client', client: queryClient }; + }, + configurable: true, + }); + } + if (Platform.OS === 'ios' || Platform.OS === 'android') { // @ts-expect-error - This is an internal API __internal_clerk.__internal_createPublicCredentials = (