New
Introducing React Doctor for Enterprise

react-doctor/tanstack-start-loader-parallel-fetch

Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to avoid request waterfalls in route loaders

  • Category: Performance
  • Severity: warn
  • Source: oxlint-plugin-react-doctor
  • Framework: tanstack-start
  • Enabled when: framework=tanstack-start and capabilities=tanstack-start

Validation prompt

Use this to decide whether a fired diagnostic is real or a false positive.

Fires when a route's loader function body contains 2 or more top-level await statements, counted across VariableDeclaration, ExpressionStatement, ReturnStatement, and for-await-of forms. Awaits nested inside if/try/loops do not count. False positive: the second await genuinely depends on data resolved by the first, in which case sequencing is required.

Fix prompt

Use this once validation confirms the diagnostic is real.

Wrap independent fetches in Promise.all: const [user, posts] = await Promise.all([fetchUser(id), fetchPosts(id)]). Keep awaits sequential only when the next call's arguments depend on the prior result. For multi-step graphs, consider batching upstream rather than chaining in the loader. See https://tanstack.com/router/latest/docs/framework/react/guide/data-loading

More Performance rules from the rules reference:

  • react-doctor/advanced-event-handler-refs: Store the handler in a ref and have the listener read `handlerRef.current()` — the subscription stays put while the latest handler is always called
  • react-doctor/async-await-in-loop: Collect the items and use `await Promise.all(items.map(...))` to run independent operations concurrently
  • react-doctor/async-defer-await: Move the `await` after the synchronous early-return guard so the skip path stays fast
  • react-doctor/async-parallel: Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to run independent operations concurrently
  • react-doctor/client-passive-event-listeners: Add `{ passive: true }` as the third argument: `addEventListener('scroll', handler, { passive: true })`. Only do this if the handler does NOT call `event.preventDefault()` — passive listeners silently ignore `preventDefault()`, which breaks features like pull-to-refresh suppression, custom gestures, and nested-scroll containment.