Skip to content

Conversation

panteliselef
Copy link
Member

@panteliselef panteliselef commented Oct 3, 2025

Description

Since the introduction of the paginated hooks and useSWR our SDKs have been suffering from a cache synchnonization issue, resulting in bad DX and stale UIs.

To help you visualize the issue, imagine a checkout flow, where the developer is using the prebuilt AIO Checkout component and then uses the useSubscription() hook to display the plan name in their custom implemented User button.

If a C2 were to checkout a paid plan and looked at their User button, the plan would still mention “Free” instead of the checkout they just paid for.

Our Plan

Per our recent discussions, we decided to move away from SWR and use React Query instead, since it allows us to correctly cache, invalidate and subscribe to changes across the host application and AIOs.

Migration path
In order to slowly migrate every hook, we are using build-time feature flag in the @clerk/shared package that controls which hook implementation is used based on the file extension. For example for the useSubscription() hooks we now have 2 files, useSubscription.rq.ts and useSubscription.swr.ts.

Once all hooks are implemented with React Query, we will start building the @clerk/shared with CLERK_USE_RQ=true in order surface the React Query implementation in the SDKs.

clerk-js
It now lazy loads QueryClient from @tanstack/query-core. That means it will only be loaded if requested by the new hooks.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features
    • Added React Query–compatible data hooks (useSubscription, useClerkQuery/useClerkQueryClient, base query utilities and types) to fetch subscription and other data.
  • Performance
    • Query client is lazy-loaded to reduce startup cost; query-core is split into its own vendor chunk.
  • Refactor
    • Provider/context wiring updated to use an SWR/React-Query compatibility layer.
  • Chores
    • Added @tanstack/query-core, build/typepath updates, and bumped bundle size threshold.

Copy link

changeset-bot bot commented Oct 3, 2025

🦋 Changeset detected

Latest commit: c07fe17

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

This PR includes changesets to release 19 packages
Name Type
@clerk/clerk-js Patch
@clerk/shared Patch
@clerk/clerk-react Patch
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/elements Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/remix Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue Patch

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

Copy link

vercel bot commented Oct 3, 2025

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

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Oct 3, 2025 6:51pm

Copy link
Contributor

coderabbitai bot commented Oct 3, 2025

Walkthrough

Adds TanStack Query Core dependency, lazy-initialized QueryClient in clerk-js with event propagation, bundler chunking, React-isomorphic exposure, new React Query hooks/types and virtualized data-hook selection (.rq vs .swr) via build-time plugin, plus related TS and build config updates.

Changes

Cohort / File(s) Change summary
Deps & thresholds
packages/clerk-js/package.json, packages/shared/package.json, packages/clerk-js/bundlewatch.config.json, .changeset/tricky-badgers-post.md
Add @tanstack/query-core (runtime in clerk-js, dev in shared). Increase dist/clerk.js maxSize by 2KB. Add patch changeset.
Bundling & build plumbing
packages/clerk-js/rspack.config.js, packages/shared/tsup.config.ts, packages/shared/tsconfig.json
Add vendor split chunk for @tanstack/query-core; add HookAliasPlugin to esbuild plugins to resolve virtual:data-hooks/* to .rq.tsx or .swr.tsx based on env flag; define __USE_RQ__; add TS path mappings and include virtual types.
Clerk JS core QueryClient
packages/clerk-js/src/core/clerk.ts, packages/clerk-js/src/core/query-core.ts
Introduce private lazy-loaded QueryClient field and public internal getter __internal_queryClient that dynamically imports local query-core and emits queryClientStatus/ready. Re-export QueryClient via query-core.ts.
React isomorphic wiring
packages/react/src/isomorphicClerk.ts, packages/react/src/contexts/ClerkContextProvider.tsx
Expose __internal_queryClient on IsomorphicClerk and propagate internal queryClientStatus events to the public event bus; minor JSX formatting change in OrganizationProvider.
RQ shared types & options
packages/shared/src/react/clerk-rq/types.ts, packages/shared/src/react/clerk-rq/queryOptions.ts
Add TanStack-aligned query option/result typings (including subscribed flag) and queryOptions overloads/aliases for initial-data / skip-token scenarios.
Clerk QueryClient access
packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
Add useClerkQueryClient hook that returns Clerk-managed QueryClient or a recursive mock proxy; subscribes to queryClientStatus events and exposes loaded state.
Base/query hooks (RQ)
packages/shared/src/react/clerk-rq/useBaseQuery.ts, packages/shared/src/react/clerk-rq/useQuery.ts
Implement useBaseQuery wiring QueryObserver with defaults and useSyncExternalStore; add useClerkQuery with overloads delegating to base.
Subscription hook (RQ/SWR) + virtualization
packages/shared/src/react/hooks/useSubscription.rq.tsx, packages/shared/src/react/hooks/useSubscription.swr.tsx, packages/shared/src/react/hooks/useSubscription.tsx, packages/shared/src/react/hooks/useSubscription.types.ts, packages/shared/src/types/virtual-data-hooks.d.ts
Add RQ and SWR implementations of useSubscription, a virtual-module re-export selecting implementation, public types for params/result, and ambient declarations for virtual:data-hooks/*.
SWR compat provider + context
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx, packages/shared/src/react/providers/SWRConfigCompat.swr.tsx, packages/shared/src/react/providers/SWRConfigCompat.tsx, packages/shared/src/react/contexts.tsx
Add virtualized SWRConfigCompat (no-op for RQ, actual SWR wrapper for SWR), re-export via virtual module, and update contexts to use SWRConfigCompat with swrConfig prop.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant Component as React Component
  participant Isomorphic as IsomorphicClerk
  participant Clerk as Clerk (clerk-js)
  participant QCore as @tanstack/query-core

  Component->>Isomorphic: access __internal_queryClient getter
  Isomorphic->>Clerk: forward __internal_queryClient access
  alt first access
    Clerk->>Clerk: dynamic import('./query-core')
    Clerk->>QCore: instantiate QueryClient
    Clerk->>Isomorphic: emit queryClientStatus / ready
    Clerk-->>Component: return { tag, queryClient }
  else already initialized
    Clerk-->>Component: return existing { tag, queryClient }
  end
  Component->>QCore: create QueryObserver / subscribe
  QCore-->>Component: observer updates -> re-render
Loading
sequenceDiagram
  autonumber
  participant Build as tsup/esbuild
  participant Plugin as HookAliasPlugin
  participant FS as fs
  participant Importer as "import 'virtual:data-hooks/useSubscription'"

  Importer->>Build: request module resolution
  Build->>Plugin: onResolve('virtual:data-hooks/useSubscription')
  Plugin->>FS: check for .rq.tsx and .swr.tsx files
  Plugin-->>Build: return resolved real file path (based on env / existence)
  Build-->>Importer: bundle chosen implementation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibbled on queries, crisp and bright,
Split them in chunks for a faster flight.
Virtual burrows choose RQ or SWR,
I hop to the right file, near or far.
Thump—events whisper "ready"—and data's in sight. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately highlights the addition of a React Query variant for the useSubscription hook, which reflects a real part of this changeset. However, it does not fully capture the broader migration of multiple SWR-based hooks and the lazy-loading of QueryClient in the shared package.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch elef/replace-swr-with-rq

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c07fe17 and c8781b7.

📒 Files selected for processing (1)
  • packages/shared/tsup.config.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/shared/tsup.config.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Packages
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

pkg-pr-new bot commented Oct 3, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@6913

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@6913

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@6913

@clerk/clerk-expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/clerk-react

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

@clerk/react-router

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

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@6913

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@6913

@clerk/types

npm i https://pkg.pr.new/@clerk/types@6913

@clerk/upgrade

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

@clerk/vue

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

commit: c07fe17

@panteliselef panteliselef changed the title feat(shared): useSubscription variant with React Query feat(shared): React Query variant for useSubscription Oct 3, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (7)
packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx (1)

55-56: Clarify the intent of commented-out __internal_queryClient prop.

The commented-out lines reference clerk.__internal_queryClient, which is part of the new internal QueryClient integration introduced in this PR. The ts-expect-error annotation indicates it is not yet typed on the clerk instance. Is this a placeholder for future work, or should it be uncommented once typing is resolved? If it's deferred, consider adding a TODO or issue reference to track completion.

Do you want me to open a tracking issue for this incomplete integration?

packages/react/src/contexts/ClerkContextProvider.tsx (1)

96-100: Clarify commented-out key and queryClient props.

The commented-out key={clerkStatus + queryStatus} and queryClient={queryClient} props suggest an incomplete migration or placeholder for future work. If these are intended for a follow-up PR, add a TODO or issue reference to track the work. Otherwise, if they are not needed, remove the comments to avoid confusion.

Do you want me to open a tracking issue for this incomplete integration, or should these comments be removed?

packages/clerk-js/package.json (1)

74-74: Align @tanstack/query-core versions across packages

  • packages/shared/package.json (line 158): bump devDependency from 5.87.4 to ^5.90.2 to match the caret range in packages/clerk-js.
  • No known security advisories for @tanstack/query-core (latest 5.90.2).
packages/shared/src/react/hooks/useSubscription.types.ts (1)

3-10: Add brief JSDoc for the for param (public API polish).

Document acceptable values ('user' | 'organization') and default behavior to match the rest of the API docs.

As per coding guidelines

packages/clerk-js/src/core/clerk.ts (1)

245-265: Harden lazy client init: add error handling and emit failure status.

Dynamic import lacks a catch; a failed import will surface as an unhandled rejection. Also consider emitting an intermediate/loading status.

Apply this diff:

   if (!this.#queryClient) {
-    void import('./query-core')
-      .then(module => module.QueryClient)
-      .then(QueryClient => {
+    void import('./query-core')
+      .then(module => module.QueryClient)
+      .then(QueryClient => {
         if (this.#queryClient) {
           return;
         }
         this.#queryClient = new QueryClient();
         // @ts-expect-error - queryClientStatus is not typed
         this.#publicEventBus.emit('queryClientStatus', 'ready');
-      });
+      })
+      .catch(err => {
+        // @ts-expect-error - queryClientStatus is not typed
+        this.#publicEventBus.emit('queryClientStatus', 'error');
+        // Optional: surface debug info
+        // console.error('Failed to initialize QueryClient', err);
+      });
   }

Optional: replace the string tag with a Symbol to avoid collisions.

packages/react/src/isomorphicClerk.ts (1)

149-150: Remove unused flag and stale prefetch comment.

prefetchQueryClientStatus is unused and the commented call references a non-existent method. Remove both to avoid confusion and possible lint failures.

Apply this diff:

-  private prefetchQueryClientStatus = false;
+  // Intentionally left out; enable if/when prefetching the internal query client is supported.

@@
-    // if (this.prefetchQueryClientStatus) {
-    //   // @ts-expect-error - queryClientStatus is not typed
-    //   this.clerkjs.getInternalQueryClient?.();
-    // }
+    // Prefetching the internal query client could be enabled in the future.
+    // Accessing `this.clerkjs?.__internal_queryClient` triggers lazy init in ClerkJS.
+    // Keep disabled until we decide on prefetch semantics.

Also applies to: 628-632

packages/shared/src/react/hooks/useSubscription.swr.tsx (1)

38-56: Gate the SWR key on user?.id for parity with RQ enabled.

Avoid creating cache entries when no user is present and skip running the fetcher entirely.

Apply this diff:

-  const swr = useSWR(
-    billingEnabled
-      ? {
-          type: 'commerce-subscription',
-          userId: user?.id,
-          args: { orgId: isOrganization ? organization?.id : undefined },
-        }
-      : null,
-    ({ args, userId }) => {
-      if (userId) {
-        return clerk.billing.getSubscription(args);
-      }
-      return null;
-    },
-    {
+  const swr = useSWR(
+    billingEnabled && user?.id
+      ? {
+          type: 'commerce-subscription',
+          userId: user.id,
+          args: { orgId: isOrganization ? organization?.id : undefined },
+        }
+      : null,
+    ({ args }) => clerk.billing.getSubscription(args),
+    {
       dedupingInterval: 1_000 * 60,
       keepPreviousData: params?.keepPreviousData,
     },
   );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3312988 and f5c27e3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • packages/clerk-js/package.json (1 hunks)
  • packages/clerk-js/rspack.config.js (1 hunks)
  • packages/clerk-js/src/core/clerk.ts (3 hunks)
  • packages/clerk-js/src/core/query-core.ts (1 hunks)
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx (1 hunks)
  • packages/react/src/contexts/ClerkContextProvider.tsx (1 hunks)
  • packages/react/src/isomorphicClerk.ts (4 hunks)
  • packages/shared/global.d.ts (1 hunks)
  • packages/shared/package.json (1 hunks)
  • packages/shared/src/react/clerk-rq/queryOptions.ts (1 hunks)
  • packages/shared/src/react/clerk-rq/types.ts (1 hunks)
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts (1 hunks)
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts (1 hunks)
  • packages/shared/src/react/clerk-rq/useQuery.ts (1 hunks)
  • packages/shared/src/react/contexts.tsx (2 hunks)
  • packages/shared/src/react/hooks/useSubscription.rq.tsx (1 hunks)
  • packages/shared/src/react/hooks/useSubscription.swr.tsx (1 hunks)
  • packages/shared/src/react/hooks/useSubscription.tsx (1 hunks)
  • packages/shared/src/react/hooks/useSubscription.types.ts (1 hunks)
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1 hunks)
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (1 hunks)
  • packages/shared/src/react/providers/SWRConfigCompat.tsx (1 hunks)
  • packages/shared/src/types/virtual-data-hooks.d.ts (1 hunks)
  • packages/shared/tsconfig.json (1 hunks)
  • packages/shared/tsup.config.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/shared/tsconfig.json
  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/clerk-js/rspack.config.js
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/clerk-js/package.json
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/shared/package.json
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
packages/*/tsconfig.json

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Type checking must be performed with TypeScript and publint.

Files:

  • packages/shared/tsconfig.json
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/clerk-js/rspack.config.js
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
packages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/types/virtual-data-hooks.d.ts
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/clerk-js/rspack.config.js
  • packages/shared/src/react/contexts.tsx
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts
  • packages/shared/global.d.ts
  • packages/shared/tsup.config.ts
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/shared/src/react/hooks/useSubscription.types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/clerk-rq/types.ts
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/clerk-js/src/core/query-core.ts
  • packages/shared/src/react/clerk-rq/queryOptions.ts
  • packages/shared/src/react/clerk-rq/useQuery.ts
  • packages/shared/src/react/clerk-rq/useBaseQuery.ts
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components

**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components: UserProfile, NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...

Files:

  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/shared/src/react/contexts.tsx
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering

Files:

  • packages/shared/src/react/hooks/useSubscription.tsx
  • packages/shared/src/react/hooks/useSubscription.swr.tsx
  • packages/react/src/contexts/ClerkContextProvider.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/shared/src/react/contexts.tsx
  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.swr.tsx
  • packages/shared/src/react/providers/SWRConfigCompat.tsx
  • packages/shared/src/react/hooks/useSubscription.rq.tsx
packages/*/package.json

📄 CodeRabbit inference engine (.cursor/rules/global.mdc)

All publishable packages should be placed under the packages/ directory

packages/*/package.json: All publishable packages must be located in the 'packages/' directory.
All packages must be published under the @clerk namespace on npm.
Semantic versioning must be used across all packages.

Files:

  • packages/clerk-js/package.json
  • packages/shared/package.json
packages/*/tsup.config.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

TypeScript compilation and bundling must use tsup.

Files:

  • packages/shared/tsup.config.ts
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/contexts/CoreClerkContextWrapper.tsx
🧬 Code graph analysis (15)
packages/clerk-js/src/core/clerk.ts (1)
packages/clerk-js/src/core/query-core.ts (1)
  • QueryClient (3-3)
packages/shared/src/react/hooks/useSubscription.swr.tsx (4)
packages/shared/src/react/hooks/useSubscription.rq.tsx (1)
  • useSubscription (22-70)
packages/shared/src/react/hooks/useSubscription.types.ts (2)
  • UseSubscriptionParams (3-10)
  • SubscriptionResult (12-21)
packages/types/src/environment.ts (1)
  • EnvironmentResource (10-24)
packages/shared/src/telemetry/events/method-called.ts (1)
  • eventMethodCalled (13-25)
packages/react/src/contexts/ClerkContextProvider.tsx (2)
packages/shared/src/react/contexts.tsx (1)
  • OrganizationProvider (108-108)
packages/shared/src/react/index.ts (1)
  • OrganizationProvider (7-7)
packages/shared/src/types/virtual-data-hooks.d.ts (5)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1)
  • SWRConfigCompat (6-8)
packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (1)
  • SWRConfigCompat (7-9)
packages/shared/src/react/hooks/useSubscription.rq.tsx (1)
  • useSubscription (22-70)
packages/shared/src/react/hooks/useSubscription.swr.tsx (1)
  • useSubscription (21-69)
packages/shared/global.d.ts (1)
  • useSubscription (17-17)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (2)
packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (1)
  • SWRConfigCompat (7-9)
packages/shared/src/types/virtual-data-hooks.d.ts (1)
  • SWRConfigCompat (3-3)
packages/shared/src/react/contexts.tsx (3)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1)
  • SWRConfigCompat (6-8)
packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (1)
  • SWRConfigCompat (7-9)
packages/shared/src/types/virtual-data-hooks.d.ts (1)
  • SWRConfigCompat (3-3)
packages/shared/src/react/clerk-rq/use-clerk-query-client.ts (1)
packages/shared/src/react/contexts.tsx (1)
  • useClerkInstanceContext (117-117)
packages/shared/global.d.ts (3)
packages/shared/src/react/hooks/useSubscription.rq.tsx (1)
  • useSubscription (22-70)
packages/shared/src/react/hooks/useSubscription.swr.tsx (1)
  • useSubscription (21-69)
packages/shared/src/types/virtual-data-hooks.d.ts (1)
  • useSubscription (4-4)
packages/shared/tsup.config.ts (1)
packages/shared/jest.config.js (1)
  • clerkJsVersion (2-2)
packages/shared/src/react/hooks/useSubscription.types.ts (1)
packages/types/src/billing.ts (1)
  • ForPayerType (69-69)
packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (3)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1)
  • SWRConfigCompat (6-8)
packages/shared/src/react/providers/SWRConfigCompat.tsx (1)
  • SWRConfigCompat (1-1)
packages/shared/src/types/virtual-data-hooks.d.ts (1)
  • SWRConfigCompat (3-3)
packages/shared/src/react/clerk-rq/queryOptions.ts (1)
packages/shared/src/react/clerk-rq/types.ts (1)
  • UseQueryOptions (27-32)
packages/shared/src/react/clerk-rq/useQuery.ts (3)
packages/shared/src/react/clerk-rq/queryOptions.ts (2)
  • DefinedInitialDataOptions (32-40)
  • UndefinedInitialDataOptions (14-21)
packages/shared/src/react/clerk-rq/types.ts (3)
  • DefinedUseQueryResult (53-53)
  • UseQueryResult (51-51)
  • UseQueryOptions (27-32)
packages/shared/src/react/clerk-rq/useBaseQuery.ts (1)
  • useBaseQuery (17-55)
packages/shared/src/react/clerk-rq/useBaseQuery.ts (2)
packages/shared/src/react/clerk-rq/types.ts (1)
  • UseBaseQueryOptions (12-24)
packages/shared/src/react/clerk-rq/use-clerk-query-client.ts (1)
  • useClerkQueryClient (82-82)
packages/shared/src/react/hooks/useSubscription.rq.tsx (8)
packages/shared/src/react/hooks/useSubscription.swr.tsx (1)
  • useSubscription (21-69)
packages/shared/src/react/hooks/useSubscription.types.ts (2)
  • UseSubscriptionParams (3-10)
  • SubscriptionResult (12-21)
packages/types/src/billing.ts (1)
  • BillingSubscriptionResource (562-612)
packages/shared/src/react/contexts.tsx (4)
  • useAssertWrappedByClerkProvider (120-120)
  • useClerkInstanceContext (117-117)
  • useUserContext (113-113)
  • useOrganizationContext (109-109)
packages/types/src/environment.ts (1)
  • EnvironmentResource (10-24)
packages/shared/src/telemetry/events/method-called.ts (1)
  • eventMethodCalled (13-25)
packages/shared/src/react/clerk-rq/use-clerk-query-client.ts (1)
  • useClerkQueryClient (82-82)
packages/shared/src/react/clerk-rq/useQuery.ts (1)
  • useClerkQuery (35-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (11)
packages/clerk-js/rspack.config.js (1)

117-122: LGTM!

The queryCoreVendor cache group correctly isolates @tanstack/query-core into its own chunk, consistent with the existing pattern for stripeVendor and other third-party libraries. The configuration appropriately uses enforce: true to ensure the chunk is always created.

packages/shared/src/react/contexts.tsx (2)

17-17: LGTM! Clean migration to SWRConfigCompat wrapper.

Replacing the direct import of SWRConfig with the new SWRConfigCompat wrapper is a clean refactor that supports both SWR and React Query implementations via the virtual module system. The public API is preserved (swrConfig prop is still passed through), and the change aligns with the overall pattern established in this PR.


69-77: LGTM! OrganizationProvider correctly uses SWRConfigCompat.

The component now renders SWRConfigCompat instead of SWRConfig, which will delegate to the appropriate implementation (SWR or React Query) based on the build-time virtual module resolution. The swrConfig prop is correctly passed through, maintaining backward compatibility.

packages/shared/src/react/providers/SWRConfigCompat.tsx (1)

1-1: Verified SWRConfigCompat.rq.tsx renders children only and SWRConfigCompat.swr.tsx wraps SWRConfig with the swrConfig prop as expected.

packages/clerk-js/src/core/query-core.ts (1)

1-3: QueryClient re-export looks good.

This keeps consumers synced with the new core integration.

packages/shared/src/react/hooks/useSubscription.types.ts (1)

12-21: Types look good.

Shape aligns with both SWR and React Query variants; using unknown for error is appropriate.

packages/shared/src/react/clerk-rq/useBaseQuery.ts (1)

28-55: Hook logic mirrors TanStack’s pattern. LGTM.

Optimistic result, useSyncExternalStore subscription, and result tracking are correct.

packages/react/src/isomorphicClerk.ts (2)

288-292: Proxy getter for internal query client is fine.

Simple passthrough is adequate for React-side access.


577-583: Good: propagate queryClientStatus listeners on hydrate.

This keeps pre-hydration listeners wired to the live ClerkJS event bus.

packages/shared/src/react/hooks/useSubscription.swr.tsx (1)

62-69: Overall SWR mapping looks good.

Return shape matches the shared SubscriptionResult. Telemetry and env gating are correct.

packages/clerk-js/src/core/clerk.ts (1)

245-265: useClerkQueryClient correctly handles undefined readiness
It subscribes to ‘queryClientStatus’, triggers a re-render via state, and falls back to a mock client until the real QueryClient is available, avoiding runtime errors.

"devDependencies": {
"@stripe/react-stripe-js": "3.1.1",
"@stripe/stripe-js": "5.6.0",
"@tanstack/query-core": "5.87.4",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Version mismatch with clerk-js package.

@tanstack/query-core is added here as exact version 5.87.4 (devDependency), but packages/clerk-js/package.json line 74 declares it as ^5.87.4 (runtime dependency). The caret range in clerk-js allows minor/patch drift, which can cause inconsistencies if shared's dev tooling expects the exact version. For workspace consistency, align both to the same specifier—either exact 5.87.4 or workspace:^.

Apply this diff to use the workspace protocol for consistency:

-    "@tanstack/query-core": "5.87.4",
+    "@tanstack/query-core": "workspace:^",

Alternatively, ensure both packages use the same exact version or caret range.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@tanstack/query-core": "5.87.4",
"@tanstack/query-core": "workspace:^",
🤖 Prompt for AI Agents
In packages/shared/package.json around line 158, the devDependency for
@tanstack/query-core is pinned to an exact version ("5.87.4") but
packages/clerk-js/package.json declares it as a caret range (^5.87.4), causing a
version specifier mismatch; update packages/shared/package.json to use the
workspace protocol (e.g., "workspace:^5.87.4") or change it to the same
specifier used in clerk-js (e.g., "^5.87.4") so both packages use an identical
dependency specifier to ensure workspace consistency.

Comment on lines +24 to +27
const observer = React.useMemo(() => {
return new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(client, defaultedOptions);
}, [client]);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure ESLint passes for hook deps (react-hooks/exhaustive-deps).

useMemo uses defaultedOptions but intentionally excludes it from deps. Add a suppression to avoid lint failures.

Apply this diff:

-  const observer = React.useMemo(() => {
+  // Intentionally exclude `defaultedOptions` to avoid re-instantiation; updates are applied via `setOptions`.
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  const observer = React.useMemo(() => {
     return new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(client, defaultedOptions);
-  }, [client]);
+  }, [client]);

Optionally include Observer in deps if your linter requires it.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const observer = React.useMemo(() => {
return new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(client, defaultedOptions);
}, [client]);
// Intentionally exclude `defaultedOptions` to avoid re-instantiation; updates are applied via `setOptions`.
// eslint-disable-next-line react-hooks/exhaustive-deps
const observer = React.useMemo(() => {
return new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(client, defaultedOptions);
}, [client]);
🤖 Prompt for AI Agents
In packages/shared/src/react/clerk-rq/useBaseQuery.ts around lines 24 to 27, the
useMemo callback references defaultedOptions (and optionally Observer) but only
lists client in the deps which triggers react-hooks/exhaustive-deps; update the
line to either add a suppression comment to ignore the exhaustive-deps rule
(e.g. // eslint-disable-next-line react-hooks/exhaustive-deps) directly above
the useMemo call, or include the missing dependencies (defaultedOptions and
optionally Observer) in the dependency array so ESLint is satisfied; ensure the
suppression is scoped only to that line if used.

Comment on lines +51 to +59
const query = useClerkQuery({
queryKey,
queryFn: ({ queryKey }) => {
const obj = queryKey[1] as { args: { orgId?: string } };
return clerk.billing.getSubscription(obj.args);
},
staleTime: 1_000 * 60,
enabled: Boolean(user?.id && billingEnabled),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore keepPreviousData behavior

Line 58: the SWR version forwarded params?.keepPreviousData to useSWR, but the React Query port drops it. That regresses the public API—callers that relied on it now lose cached data and see loading flicker on refetch. Please pass the flag to the query options just like before.

   const query = useClerkQuery({
     queryKey,
     queryFn: ({ queryKey }) => {
       const obj = queryKey[1] as { args: { orgId?: string } };
       return clerk.billing.getSubscription(obj.args);
     },
     staleTime: 1_000 * 60,
     enabled: Boolean(user?.id && billingEnabled),
+    keepPreviousData: params?.keepPreviousData,
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const query = useClerkQuery({
queryKey,
queryFn: ({ queryKey }) => {
const obj = queryKey[1] as { args: { orgId?: string } };
return clerk.billing.getSubscription(obj.args);
},
staleTime: 1_000 * 60,
enabled: Boolean(user?.id && billingEnabled),
});
const query = useClerkQuery({
queryKey,
queryFn: ({ queryKey }) => {
const obj = queryKey[1] as { args: { orgId?: string } };
return clerk.billing.getSubscription(obj.args);
},
staleTime: 1_000 * 60,
enabled: Boolean(user?.id && billingEnabled),
keepPreviousData: params?.keepPreviousData,
});
🤖 Prompt for AI Agents
In packages/shared/src/react/hooks/useSubscription.rq.tsx around lines 51 to 59,
the React Query port dropped support for params?.keepPreviousData (previously
forwarded to useSWR), causing callers to lose cached data and see loading
flicker; restore behavior by passing the keepPreviousData flag into the
useClerkQuery options (e.g., add keepPreviousData: params?.keepPreviousData
alongside queryKey, queryFn, staleTime, enabled) so callers that set that param
retain previous data during refetch.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/clerk-js/bundlewatch.config.json (1)

3-3: Add bundlewatch coverage for the new TanStack Query chunk.

We just split out a query-core vendor chunk, but bundlewatch still has no entry for it. Without a limit here, any future growth in that chunk will go unnoticed. Please add a { "path": "./dist/query-core-vendors*.js", ... } (or whatever the final filename is) entry with an appropriate ceiling when bumping this maxSize.

packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1)

4-6: Add documentation explaining the compatibility shim's purpose.

The @internal marker is appropriate, but the JSDoc should explain that this is a no-op compatibility shim for the React Query variant, allowing seamless switching between SWR and React Query implementations via the virtual module system.

Apply this diff to improve the documentation:

 /**
+ * No-op compatibility shim for React Query variant. When using React Query,
+ * QueryClientProvider is used elsewhere, so this simply passes children through.
+ * The SWR variant wraps children with SWRConfig for configuration.
  * @internal
  */
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f5c27e3 and 96b68d2.

📒 Files selected for processing (5)
  • .changeset/tricky-badgers-post.md (1 hunks)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
  • packages/react/src/contexts/ClerkContextProvider.tsx (1 hunks)
  • packages/react/src/isomorphicClerk.ts (3 hunks)
  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/tricky-badgers-post.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/src/contexts/ClerkContextProvider.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
  • packages/clerk-js/bundlewatch.config.json
packages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components

**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components: UserProfile, NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
  • packages/react/src/isomorphicClerk.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering

Files:

  • packages/shared/src/react/providers/SWRConfigCompat.rq.tsx
🧬 Code graph analysis (1)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (2)
packages/shared/src/react/providers/SWRConfigCompat.swr.tsx (1)
  • SWRConfigCompat (7-9)
packages/shared/src/types/virtual-data-hooks.d.ts (1)
  • SWRConfigCompat (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
packages/shared/src/react/providers/SWRConfigCompat.rq.tsx (1)

1-2: LGTM with a minor note.

The type-only import for PropsWithChildren follows TypeScript best practices. The React import is needed for JSX, though with React 17+ and the new JSX transform configured, explicit React imports may be unnecessary.

Comment on lines +576 to +581
// @ts-expect-error - queryClientStatus is not typed
this.#eventBus.internal.retrieveListeners('queryClientStatus')?.forEach(listener => {
// Since clerkjs exists it will call `this.clerkjs.on('queryClientStatus', listener)`
// @ts-expect-error - queryClientStatus is not typed
this.on('queryClientStatus', listener, { notify: true });
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Ensure queryClientStatus event is properly typed in a follow-up.

The queryClientStatus event propagation uses @ts-expect-error suppressions, indicating the event is not yet part of the type system. While this mirrors the pattern used for status event listeners (lines 571-574), the event name and callback signature should be properly typed to maintain type safety.

Verify that:

  1. The queryClientStatus event is added to the Clerk event type definitions
  2. The listener callback signature is properly typed
  3. A follow-up task exists to remove these @ts-expect-error suppressions

You can run this script to check if the event is documented elsewhere:


🏁 Script executed:

#!/bin/bash
# Search for queryClientStatus event definitions and usages
rg -nC3 "queryClientStatus" --type=ts -g '!*.test.ts'

Length of output: 148


🏁 Script executed:

#!/bin/bash
rg -nC3 "queryClientStatus"

Length of output: 2581


Define and type the queryClientStatus event

Add queryClientStatus to Clerk’s event type definitions and remove the @ts-expect-error suppressions in:

  • packages/clerk-js/src/core/clerk.ts (emit)
  • packages/react/src/isomorphicClerk.ts (propagation)
  • packages/shared/src/react/clerk-rq/use-clerk-query-client.ts (hook)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/shared/src/react/clerk-rq/types.ts (1)

26-33: Consider adding a brief explanation for the ESLint disable.

The ESLint disable for no-empty-object-type is necessary here since UseQueryOptions intentionally creates a specialized type by omitting properties. However, adding a brief inline comment would improve maintainability.

Apply this diff to add explanatory context:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- Empty interface intentionally refines UseBaseQueryOptions by omitting 'suspense'
 export interface UseQueryOptions<
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 96b68d2 and c07fe17.

📒 Files selected for processing (1)
  • packages/shared/src/react/clerk-rq/types.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/shared/src/react/clerk-rq/types.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/shared/src/react/clerk-rq/types.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/shared/src/react/clerk-rq/types.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/shared/src/react/clerk-rq/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/shared/src/react/clerk-rq/types.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/shared/src/react/clerk-rq/types.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/shared/src/react/clerk-rq/types.ts (4)

1-9: LGTM!

The type-only imports follow best practices and align with the coding guideline for using import type syntax.


11-24: LGTM!

The UseBaseQueryOptions interface properly extends QueryObserverOptions and adds a well-documented subscribed property. The type alias and generics follow TanStack Query conventions.


35-48: LGTM!

The UseInfiniteQueryOptions interface correctly extends InfiniteQueryObserverOptions and consistently applies the subscribed property pattern from UseBaseQueryOptions. The infinite query-specific TPageParam generic is properly included.


50-54: LGTM!

The result type aliases provide consistent naming and allow for future extensibility through the indirection layer (UseQueryResultUseBaseQueryResultQueryObserverResult).

Copy link

@Ephem Ephem left a comment

Choose a reason for hiding this comment

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

Great work on this, these are just a few random smaller comments I saw when skimming through it.

<SessionContext.Provider value={sessionCtx}>
<OrganizationProvider {...organizationCtx.value}>
<OrganizationProvider
// key={clerkStatus + queryStatus}
Copy link

Choose a reason for hiding this comment

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

NIT: ✂️

if (this.#queryClient) {
return;
}
this.#queryClient = new QueryClient();
Copy link

Choose a reason for hiding this comment

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

I think there are options we might want to consider here, like retry logic. Do we want to always retry all errors, or ignore retrying 4xx kind of errors for example? How many retries should we do by default? Is the default retry delay good? Do we want to match these closely to what we have in SWR today, or change them as part of this?

To be clear, the default RQ ones does the job so I don't think we need to, especially at this early stage, just wanted to mention it.

if (this.#queryClient) {
return;
}
this.#queryClient = new QueryClient();
Copy link

Choose a reason for hiding this comment

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

If we want revalidation on focus and the network status things to work, you will also need to call queryClient.mount(). This is normally done in QueryClientProvider but since we are skipping that here we'd need to do it manually if we need it.

This is safe but a noop for native environments.

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.

3 participants