Skip to content

feat: add useAuth0Suspense hook for handling auth loading state with React 19+ - #1184

Open
gyaneshgouraw-okta wants to merge 2 commits into
mainfrom
auth0-suspense
Open

feat: add useAuth0Suspense hook for handling auth loading state with React 19+#1184
gyaneshgouraw-okta wants to merge 2 commits into
mainfrom
auth0-suspense

Conversation

@gyaneshgouraw-okta

@gyaneshgouraw-okta gyaneshgouraw-okta commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds useAuth0Suspense, a React 19-only hook that lets components read auth state declaratively: a <Suspense> boundary handles the loading state and a React Error Boundary handles initialization errors, removing the manual isLoading / error checks that useAuth0 requires.
The change is additive and non-breaking, the core path (Auth0Provider, useAuth0) stays compatible with React 16.11–19, peerDependencies is unchanged, and the new hook is gated at runtime so React 16–18 consumers get a clear error instead of a crash if they call it.

Changes

  • Auth0Provider now creates a stable initialization promise that resolves when auth init succeeds and rejects (with the same error surfaced on error) when it fails, exposed as an internal, unsupported context field.
  • New useAuth0Suspense() hook reads that promise via React 19's use(): it suspends until auth is ready (so the nearest <Suspense> fallback renders) and re-throws init errors to the nearest Error Boundary.
  • Returns the full useAuth0 interface minus isLoading (always resolved) and error (thrown), including all auth methods (loginWithRedirect, logout, getAccessTokenSilently, …).
  • Runtime guards: throws a clear "requires React 19" error when React.use is unavailable, and a "must be used within an <Auth0Provider>" error when used outside a provider.
  • Exported from the package root alongside its Auth0SuspenseContextInterface type.
  • Documented with a Suspense usage example in the examples doc.

Example

import { Suspense } from 'react';
import { Auth0Provider, useAuth0Suspense } from '@auth0/auth0-react';

function App() {
  return (
    <Auth0Provider domain={domain} clientId={clientId} authorizationParams={{ redirect_uri: window.location.origin }}>
      <MyErrorBoundary fallback={<p>Could not sign you in.</p>}>
        <Suspense fallback={<p>Loading…</p>}>
          <UserGreeting />
        </Suspense>
      </MyErrorBoundary>
    </Auth0Provider>
  );
}

function UserGreeting() {
  const { user, isAuthenticated } = useAuth0Suspense();
  return isAuthenticated ? <p>Hello, {user?.name}!</p> : <p>Please log in</p>;
}

Testing

  • npm test - full suite passing at 100% coverage.
  • New tests cover the hook's full behavior, its two success/pending render paths, both init-error paths routing to an Error Boundary, both runtime guards, and its export surface
  • Existing Auth0Provider tests extended to verify the init promise resolves on success, rejects on failure, and keeps a stable reference across re-renders.

@gyaneshgouraw-okta
gyaneshgouraw-okta requested a review from a team as a code owner July 29, 2026 08:34
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Auth0Provider now exposes initialization through an internal promise, and the new React 19 useAuth0Suspense hook uses it to suspend during loading and propagate failures to error boundaries. Tests and documentation cover the new behavior, exports, compatibility checks, and usage example.

Changes

Auth0 Suspense integration

Layer / File(s) Summary
Initialization promise
src/auth0-context.tsx, src/auth0-provider.tsx, __tests__/auth-provider.test.tsx
The provider exposes a stable _initPromise that resolves after successful initialization and rejects when initialization fails.
Suspense hook and exports
src/use-auth0-suspense.tsx, src/index.tsx
useAuth0Suspense requires React 19, suspends on initialization, propagates errors, omits isLoading and error, and is exported from the package root.
Behavior tests and documentation
__tests__/use-auth0-suspense.test.tsx, EXAMPLES.md
Tests cover loading, success, initialization failures, provider and React version errors, and exports; documentation adds a React 19 usage example.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: piyush-85

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant Auth0Provider
  participant Auth0Client
  participant useAuth0Suspense
  participant Suspense
  participant ErrorBoundary

  Application->>Auth0Provider: render application
  Auth0Provider->>Auth0Client: initialize Auth0
  Application->>useAuth0Suspense: read authentication context
  useAuth0Suspense->>Auth0Provider: read _initPromise
  useAuth0Suspense-->>Suspense: suspend while initialization is pending
  Auth0Client-->>Auth0Provider: resolve or reject initialization
  Auth0Provider-->>useAuth0Suspense: provide initialized context or error
  Suspense-->>Application: render authenticated content
  ErrorBoundary-->>Application: render initialization error
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a new React 19+ useAuth0Suspense hook for auth loading and error handling.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth0-suspense

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/use-auth0-suspense.tsx`:
- Around line 10-13: Update Auth0SuspenseContextInterface and the
useAuth0Suspense return destructuring to omit _initPromise alongside isLoading
and error, ensuring the internal field is excluded from both the public type and
runtime result.
- Line 1: Update the import in use-auth0-suspense.tsx to use a React namespace
import, and change the hook’s use invocation to React.use while preserving
useContext and the existing behavior. This avoids requiring a static named use
export from older supported React versions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c9f4d54-bc0f-4e6e-8254-1c70f6a0094b

📥 Commits

Reviewing files that changed from the base of the PR and between 361c018 and 7eeb79a.

📒 Files selected for processing (7)
  • EXAMPLES.md
  • __tests__/auth-provider.test.tsx
  • __tests__/use-auth0-suspense.test.tsx
  • src/auth0-context.tsx
  • src/auth0-provider.tsx
  • src/index.tsx
  • src/use-auth0-suspense.tsx

@@ -0,0 +1,59 @@
import { use, useContext } from 'react';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does importing a named export that is absent in an older CommonJS React version (e.g. use) via import { use } from 'react' trigger build errors/warnings under bundlers with strict export-presence checks (e.g. Create React App / webpack strictExportPresence)?

💡 Result:

Yes, importing a named export that does not exist in an older version of React (such as import { use } from 'react' in React 18 or earlier) will trigger a build error under bundlers configured with strict export-presence checks [1][2]. When strictExportPresence is enabled in Webpack—a configuration used by default in older versions of Create React App—a missing named export is elevated from a build-time warning to a hard compilation error [3][4][5]. ### Why this happens 1. Static Analysis: Modern bundlers like Webpack perform static analysis on your code to verify imports. When they encounter an import statement for a named export, they check the target module (in this case, the react package) to ensure that the export exists [5][6]. 2. Strict Presence Checks: When module.strictExportPresence is set to true, the bundler strictly enforces that all imported members must be present in the target module [3][7]. If a library (such as a router or UI library) uses a new API like use() that is not available in the installed version of React, the bundler identifies this discrepancy during the build process and halts compilation [1][2]. ### Common Scenarios * Create React App (CRA): Older versions of CRA specifically enabled strictExportPresence to ensure that developers were alerted to missing imports, which helps prevent runtime issues [4][5]. * Dependency Incompatibility: This issue is frequently encountered when upgrading third-party dependencies (like react-router v7) that utilize new features (like the React 19 use hook) while the project is still running an older version of React (e.g., React 18) [1][2]. ### How to Resolve * Upgrade React: The most direct fix is to upgrade react and react-dom to a version that supports the required exports (e.g., React 19 for the use hook) [1][8][9]. * Adjust Bundler Configuration: If an immediate upgrade is not possible, you can temporarily downgrade the error to a warning by adjusting your Webpack configuration [2]: javascript // In your webpack.config.js module: { parser: { javascript: { importExportsPresence: 'warn' // Or 'false' to suppress } } } Note that setting this to false or warn suppresses the build error but may lead to runtime undefined errors if the code attempts to call the missing import [5][2][10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## package files"
git ls-files | rg '(^|/)(package\.json|tsconfig.*\.json|vite\.config\.[jt]s|webpack\.config\.[jt]s|rollup\.config\.[jt]s|babel\.config\.[jt]s|\.npmrc|pnpm-workspace\.yaml)$' || true

echo
echo "## relevant package.json react version mentions"
python3 - <<'PY'
import json, pathlib
for p in pathlib.Path('.').rglob('package.json'):
    try:
        data=json.loads(p.read_text())
    except Exception as e:
        continue
    keys=set()
    for sec in ('dependencies','devDependencies','peerDependencies','optionalDependencies'):
        if isinstance(data.get(sec), dict):
            keys.update(k for k,v in data[sec].items() if k=='react' or 'react' in k)
    if keys:
        print(p)
        for sec in ('dependencies','devDependencies','peerDependencies','optionalDependencies'):
            vals={k:data[sec][k] for k in keys if k in data.get(sec, {})}
            if vals:
                print("  ", sec, "=>", vals)
PY

echo
echo "## target file outline and contents"
file="$(git ls-files | rg '(^|/)use-auth0-suspense\.tsx$' | head -n1 || true)"
if [ -n "$file" ]; then
  wc -l "$file"
  ast-grep outline "$file" || true
  echo
  cat -n "$file"
fi

echo
echo "## named import usages of react/use"
rg -n "import \{[^}]*\buse\b[^\}]*\} from ['\"]react['\"]|React\.use|useHook|typeof use|strictExportPresence|importExportsPresence" . -g '!node_modules' -g '!dist' -g '!build' || true

Repository: auth0/auth0-react

Length of output: 11380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## react package shape in node_modules"
python3 - <<'PY'
import json, pathlib
print("## react package.json")
try:
    p=pathlib.Path("node_modules/react/package.json")
    data=json.loads(p.read_text())
    print(data.get("exports"))
    print(data.get("module"))
    print([k for k in data if k in ("exports","types","type","main","module","name","version")])
except Exception as e:
    print("missing node_modules/react/package.json:", e)

print("\n## exports snippets")
for p in pathlib.Path("node_modules/react").glob("**"):
    if p.is_file() and p.name in ("index.cjs","index.production.min.js","index.development.js","index.js","package.json"):
        text=p.read_text(errors="ignore")
        if "use" in text:
            print("\n---", p, "---")
            lines=text.splitlines()
            for i,l in enumerate(lines,1):
                if "use" in l.lower() and ("export" in l or "function" in l or "useContext" in l or "React.createElement" in l):
                    print(i,l)
            txt="\n".join(lines[:220])
            print(txt[-1500:])
PY

echo
echo "## test around React.use unavailable"
testf="$(git ls-files | rg '__tests__/use-auth0-suspense\.test\.tsx$' | head -n1 || true)"
if [ -n "$testf" ]; then
  wc -l "$testf"
  sed -n '80,140p' "$testf" | cat -n
fi

Repository: auth0/auth0-react

Length of output: 2849


Use React.use for React 16/17/18 compatibility.

import { use } from 'react' requires the static named export added in React 19. This hook is documented for React 19+, but the package still supports down to React 16.11 and tests cover unavailable use; consumers with older React can fail build-time named-export checks, so use a namespace import and call React.use to avoid breaking React 18 and older consumers for an optional feature.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/use-auth0-suspense.tsx` at line 1, Update the import in
use-auth0-suspense.tsx to use a React namespace import, and change the hook’s
use invocation to React.use while preserving useContext and the existing
behavior. This avoids requiring a static named use export from older supported
React versions.

Comment on lines +10 to +13
export type Auth0SuspenseContextInterface<TUser extends User = User> = Omit<
Auth0ContextInterface<TUser>,
'isLoading' | 'error'
>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

_initPromise leaks into the public type and runtime return value.

The docstring promises the hook "returns the full useAuth0 interface minus isLoading ... and error", but Auth0SuspenseContextInterface's Omit list and the { isLoading, error, ...rest } destructure both leave out _initPromise. Since _initPromise is documented as internal/not-public-API on Auth0ContextInterface, it should not be exposed through this hook's type or return value either.

🔧 Proposed fix
 export type Auth0SuspenseContextInterface<TUser extends User = User> = Omit<
   Auth0ContextInterface<TUser>,
-  'isLoading' | 'error'
+  'isLoading' | 'error' | '_initPromise'
 >;
-  // eslint-disable-next-line `@typescript-eslint/no-unused-vars`
-  const { isLoading, error, ...rest } = ctx;
+  // eslint-disable-next-line `@typescript-eslint/no-unused-vars`
+  const { isLoading, error, _initPromise, ...rest } = ctx;
   return rest;

Also applies to: 54-56

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/use-auth0-suspense.tsx` around lines 10 - 13, Update
Auth0SuspenseContextInterface and the useAuth0Suspense return destructuring to
omit _initPromise alongside isLoading and error, ensuring the internal field is
excluded from both the public type and runtime result.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant