react-doctor/tanstack-start-no-useeffect-fetch
Fetch data in the route `loader` instead — the router coordinates loading before rendering to avoid waterfalls
- Category: TanStack Start
- 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 useEffect, useLayoutEffect, or useInsertionEffect inside a /routes/ file contains a bare fetch(...) call anywhere in its callback. Wrapped clients like axios.get are not detected. False positive: a client-only subscription bootstrap (SSE, WebSocket handshake) that genuinely must run after mount and cannot move to a loader.
Fix prompt
Use this once validation confirms the diagnostic is real.
Move the request into the route loader (or a createServerFn called from the loader) so the router resolves data before rendering and avoids the mount-then-fetch waterfall. For client-driven, cache-aware data, prefer TanStack Query's useQuery. See https://tanstack.com/router/latest/docs/framework/react/guide/data-loading
Related rules
More TanStack Start rules from the rules reference:
react-doctor/tanstack-start-redirect-in-try-catch: TanStack Router's `redirect()` and `notFound()` throw special errors caught by the router. Move them outside the try block or re-throw in the catchreact-doctor/tanstack-start-route-property-order: Follow the order: params/validateSearch → loaderDeps → context → beforeLoad → loader → head. See https://tanstack.com/router/latest/docs/eslint/create-route-property-orderreact-doctor/tanstack-start-server-fn-method-order: Chain methods in order: .middleware() → .inputValidator() → .client() → .server() → .handler() — types depend on this sequencereact-doctor/tanstack-start-server-fn-validate-input: Add `.inputValidator(schema)` before `.handler()` — data crosses a network boundary and must be validated at runtimereact-doctor/tanstack-start-missing-head-content: Add `<HeadContent />` inside `<head>` in your __root route — without it, route `head()` meta tags are silently dropped