Skip to content

feat(react-router): allow pendingComponent: false to opt out of the default pending fallback - #8093

Open
nishant-batra wants to merge 3 commits into
TanStack:mainfrom
nishant-batra:fix-pending-component-opt-out
Open

feat(react-router): allow pendingComponent: false to opt out of the default pending fallback#8093
nishant-batra wants to merge 3 commits into
TanStack:mainfrom
nishant-batra:fix-pending-component-opt-out

Conversation

@nishant-batra

@nishant-batra nishant-batra commented Aug 17, 2026

Copy link
Copy Markdown

Description

Fixes #7773.

Adds a way to opt a route out of the router-wide default pending fallback entirely, using the same convention errorComponent already ships with:

// packages/react-router/src/route.tsx
errorComponent?: false | null | undefined | ErrorRouteComponent
pendingComponent?: false | null | undefined | RouteComponent // <- this PR

Why this is a one-line, logic-free change

Both pendingComponent and errorComponent are read through the same ?? default pattern, and ?? only falls through on null/undefinedfalse is not nullish, so it survives.

  • Match.tsx#renderPending (render): route?.options.pendingComponent ?? router.options.defaultPendingComponent, then if (!PendingComponent) return null.
  • load-client.ts (pending-status timing calc): same ??, then if (!component ...) return.

false ?? default stays false, and the subsequent truthy checks already treat falsy as "nothing to render" — same as errorComponent's routeErrorComponent ? CatchBoundary : SafeFragment. So the runtime already does the right thing with false; it's only blocked by the type today. No changes needed in either file, confirmed by tracing every reference to .pendingComponent in the React path.

Test plan

Added packages/react-router/tests/issue-7773-pending-component-opt-out.test.tsx:

  • A route with pendingComponent: false never shows the router-wide defaultPendingComponent, even with defaultPendingMs: 0, and still renders its real content once the loader resolves.
  • A contrast case: a sibling route without the opt-out still shows the default normally, confirming the type widening doesn't disturb default inheritance.

Ran together with the existing pending/error-boundary suites — 64/64 passing, no regressions.

  • test:types (tsc on src) — clean
  • eslint on changed files — clean
  • prettier --check — clean
  • vitest run on the new + related pending/error test files — 64/64 pass

Status

Opening as a draft — proposed this approach on #7773 and haven't gotten maintainer sign-off yet, since it's technically an API surface change per CONTRIBUTING.md. Marking draft rather than requesting review until that lands.

AI assistance

Used AI to help trace the ??/fallback-chain call sites and draft the diff and test. Reviewed and verified the control-flow claims myself and can walk through the reasoning in review.

Summary by CodeRabbit

  • New Features

    • Routes can now opt out of displaying the router’s default pending/loading component by setting pendingComponent to false.
    • Routes without this setting continue to use the shared pending fallback.
  • Documentation

    • Updated route option documentation to describe the new opt-out behavior.

…efault pending fallback

Fixes TanStack#7773. Mirrors the existing errorComponent?: false | null | undefined | ErrorRouteComponent
pattern. false survives the ?? fallback chain in Match.tsx and load-client.ts (only null/undefined
are nullish), so the runtime already treats it as "render nothing" -- this widens the type to
allow passing it.
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e938bf62-b782-490c-86e3-3bb87db4d673

📥 Commits

Reviewing files that changed from the base of the PR and between 8faea26 and 5c44c9a.

📒 Files selected for processing (1)
  • packages/react-router/tests/issue-7773-pending-component-opt-out.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-router/tests/issue-7773-pending-component-opt-out.test.tsx

Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The route option contract and documentation now support explicit pending-component opt-out values. Tests verify that pendingComponent: false suppresses the router-wide pending fallback while routes without the option retain it.

Changes

Pending component opt-out

Layer / File(s) Summary
Route option contract
packages/react-router/src/route.tsx, docs/router/api/router/RouteOptionsType.md
The pendingComponent declaration accepts false, null, and undefined. The documentation describes false as disabling the default pending component.
Pending state validation
packages/react-router/tests/issue-7773-pending-component-opt-out.test.tsx
Tests verify that pendingComponent: false hides the router-wide pending fallback during loading and that sibling routes without the option still display it.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 5c44c

This localized change allows routes to opt out of the default pending fallback while preserving normal fallback inheritance for other routes; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes adding false as an opt-out from the router-wide pending fallback.
Linked Issues check ✅ Passed The changes implement the explicit pending-component opt-out requested by issue #7773 and verify fallback inheritance behavior.
Out of Scope Changes check ✅ Passed The documentation, public type updates, and tests directly support the pendingComponent opt-out objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Updates the RouteOptions API reference for TanStack#7773 -- false now opts
a route out of routerOptions.defaultPendingComponent entirely.
@nishant-batra
nishant-batra force-pushed the fix-pending-component-opt-out branch from 37f9414 to 8faea26 Compare August 17, 2026 15:40
@nishant-batra
nishant-batra marked this pull request as ready for review August 17, 2026 17:36
render(<RouterProvider router={router} />)

await act(async () => {
await vi.advanceTimersByTimeAsync(0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

advancing by 0? does that do anything? same below

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right, advancing by 0ms doesn't do anything, that was missed before. Thanks for flagging it, fixed now in 5c44c9a.

…mponent test

The opt-out case never schedules a pending timer (pendingComponent: false
short-circuits before pendingMs/pendingMinMs come into play), so it doesn't
need fake timers at all. The default-inheritance case only needs fake timers
to cross the pendingMinMs boundary, tightened to a 99ms/+1ms check.
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.

Add a way to opt out of the default pending component

2 participants