Skip to content

Conversation

@leesb971204
Copy link
Contributor

@leesb971204 leesb971204 commented Jul 22, 2025

fixes #4744

Summary by CodeRabbit

  • New Features

    • Not-found components now receive spread props for direct access to payload fields.
    • Not-found component props updated: data is optional; isNotFound and routeId are provided.
    • Added a notFound helper and exported typing for not-found component props.
  • Documentation

    • Added a new NotFoundComponent API page and updated React Router docs to list it.
  • Examples

    • Demo showing customizable not-found UI for user lookups.
  • Tests

    • Added runtime and TypeScript tests validating not-found prop shapes and spread behavior.

@nx-cloud
Copy link

nx-cloud bot commented Jul 22, 2025

View your CI Pipeline Execution ↗ for commit b0b6c9e

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 4m 48s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 29s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-19 18:45:01 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jul 22, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4746

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4746

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4746

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4746

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@4746

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4746

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4746

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@4746

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4746

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4746

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4746

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4746

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4746

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4746

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4746

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4746

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4746

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@4746

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4746

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4746

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4746

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4746

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4746

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@4746

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4746

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4746

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4746

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4746

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@4746

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4746

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@4746

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@4746

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4746

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4746

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4746

commit: b0b6c9e

@leesb971204 leesb971204 marked this pull request as draft July 22, 2025 10:22
Signed-off-by: leesb971204 <leesb971204@gmail.com>
@leesb971204 leesb971204 force-pushed the fix/nested-notFound-data branch from 04e715a to f907711 Compare July 22, 2025 11:14
@leesb971204 leesb971204 changed the title fix(react-router): update error handling in MatchInner to access error data directly fix(: update error handling in MatchInner to access error data directly Jul 22, 2025
@leesb971204 leesb971204 changed the title fix(: update error handling in MatchInner to access error data directly fix: update error handling in MatchInner to access error data directly Jul 22, 2025
Signed-off-by: leesb971204 <leesb971204@gmail.com>
@leesb971204 leesb971204 marked this pull request as ready for review July 22, 2025 12:05
if (!route.options.notFoundComponent) {
if (router.options.defaultNotFoundComponent) {
return <router.options.defaultNotFoundComponent data={data} />
return <router.options.defaultNotFoundComponent {...data} />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part causes the structure to become nested like data: { data: ... }.

@leesb971204
Copy link
Contributor Author

@schiller-manuel
@SeanCassiere

Is there anything else I should do to get a review on this?

@schiller-manuel
Copy link
Contributor

thanks for this PR!
can you please

  • add some runtime tests for the various scenarios
  • add a typetest maybe? not sure if that makes sense / is possible
  • update docs
  • add usage to the kitchen sink example

@leesb971204 leesb971204 marked this pull request as draft August 2, 2025 10:08
@leesb971204 leesb971204 force-pushed the fix/nested-notFound-data branch from 28b5de3 to a3bdffd Compare August 2, 2025 10:24
…s data

Signed-off-by: leesb971204 <leesb971204@gmail.com>
…nent

Signed-off-by: leesb971204 <leesb971204@gmail.com>
@github-actions github-actions bot added the documentation Everything documentation related label Aug 2, 2025
…and isNotFound, routeId

Signed-off-by: leesb971204 <leesb971204@gmail.com>
@leesb971204 leesb971204 marked this pull request as ready for review August 3, 2025 12:10
…enerics

Signed-off-by: leesb971204 <leesb971204@gmail.com>
…or data props

Signed-off-by: leesb971204 <leesb971204@gmail.com>
Signed-off-by: leesb971204 <leesb971204@gmail.com>
…PI reference

Signed-off-by: leesb971204 <leesb971204@gmail.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Walkthrough

Adds documentation for a NotFoundComponent; changes React and Solid renderers to spread not-found payload props into not-found components; extends NotFoundRouteProps with optional data, isNotFound, and routeId; adds tests and example usage validating data propagation and exports a notFound helper and NotFoundRouteProps type.

Changes

Cohort / File(s) Summary
Docs: React API & component page
docs/router/framework/react/api/router.md, docs/router/framework/react/api/router/notFoundComponentComponent.md
Add NotFoundComponent entry and a new page documenting props (data?: unknown, isNotFound: boolean, routeId) and typical render outcomes.
Render: React & Solid not-found prop spread
packages/react-router/src/renderRouteNotFound.tsx, packages/solid-router/src/renderRouteNotFound.tsx
Invoke default and per-route notFound components with spread props ({...data}) instead of a single data={data} prop.
Core types: not-found props
packages/router-core/src/route.ts
Update NotFoundRouteProps: data -> data?: unknown; add isNotFound: boolean and routeId: RouteIds<RegisteredRouter['routeTree']>; import RouteIds.
Tests: not-found behavior & types
packages/react-router/tests/not-found.test.tsx, packages/router-core/tests/load.test-d.ts
Add runtime tests verifying spread-style prop delivery to default and per-route notFound components; add type-level tests for NotFoundRouteProps. Tests import/verify notFound helper and export/type usage.
Examples: kitchen-sink not-found demo
examples/react/kitchen-sink/src/main.tsx
Use notFound({ data }) in a loader; add UsersNotFoundComponent, NotFoundRouteProps typing, MissingUserData type guard, and register notFoundComponent on the users route; include in-app demo links.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Loader as Route Loader
  participant Router as Router
  participant Renderer as renderRouteNotFound
  participant NotFoundComp as NotFoundComponent

  User->>Loader: navigate
  Loader-->>Router: throw notFound({ data: ... })
  Router->>Renderer: renderRouteNotFound(notFoundPayload)
  note right of Renderer #D6F5D6: New: spread payload into props<br/>{...data, isNotFound, routeId}
  Renderer->>NotFoundComp: call NotFoundComponent({...data, isNotFound, routeId})
  NotFoundComp-->>User: render UI using props
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • schiller-manuel

Poem

A hop, a skip, a not-found trail,
Props now spread so views prevail.
routeId, flag, and user data near,
I twitch my whiskers — tests cheer! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ❓ Inconclusive The linked issue asked for removal of the redundant nesting (props.data.data) and improved typing so consumers can access user-provided payloads directly; the PR updates runtime behavior to spread the notFound payload into component props (react and solid renderRouteNotFound changes), updates NotFoundRouteProps to include data?: unknown, isNotFound, and routeId, and adds runtime and type tests plus docs and an example demonstrating direct access, which addresses the nesting and typing concerns; however the summaries show NotFoundRouteProps uses unknown rather than an explicitly generic T tied to notFound, so it is unclear from the provided diffs whether the PR fully implements the requested generic-aware typing that propagates T from notFound into component props. If the intent is to preserve the user-supplied type via generics, add or document an explicit generic NotFoundRouteProps (or show a type-level test demonstrating T flows from notFound(...) to the notFound component props) so reviewers can verify the generic typing behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title states the intent to "access error data directly" which matches the PR's core work of surfacing notFound error payloads directly to components (runtime changes to spread notFound payload into component props, typing updates, tests, docs, and example updates); however the specific identifier "MatchInner" does not appear in the provided diffs, so the label is slightly implementation-specific but still accurately describes the intent.
Out of Scope Changes Check ✅ Passed All modified files and added artifacts in the summaries (renderRouteNotFound changes for react/solid, router-core typing updates, new type/runtime tests, docs page, and example usage) are directly related to fixing notFound payload handling, typing, and documentation; I do not see unrelated or out-of-scope feature changes in the provided summaries.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Signed-off-by: leesb971204 <leesb971204@gmail.com>
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 (2)
examples/react/kitchen-sink/src/main.tsx (2)

45-51: Tighten the type guard to exclude NaN/non-integers

Use Number.isInteger to align with zod’s int and avoid NaN.

-function isMissingUserData(data: unknown): data is MissingUserData {
-  return (
-    typeof data === 'object' &&
-    data !== null &&
-    typeof (data as { userId?: unknown }).userId === 'number'
-  )
-}
+function isMissingUserData(data: unknown): data is MissingUserData {
+  return (
+    typeof data === 'object' &&
+    data !== null &&
+    Number.isInteger((data as { userId?: unknown }).userId as number)
+  )
+}

53-72: Optionally type the not-found props to your payload and simplify access

If NotFoundRouteProps is generic (per this PR’s intent), parameterizing removes the runtime guard.

-function UsersNotFoundComponent({ data, routeId }: NotFoundRouteProps) {
-  const userId = isMissingUserData(data) ? data.userId : undefined
+function UsersNotFoundComponent({ data, routeId }: NotFoundRouteProps<MissingUserData>) {
+  const userId = data?.userId

Please confirm NotFoundRouteProps is available in this branch; otherwise ignore.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2b8ee7 and a29b905.

📒 Files selected for processing (1)
  • examples/react/kitchen-sink/src/main.tsx (5 hunks)
🔇 Additional comments (6)
examples/react/kitchen-sink/src/main.tsx (6)

14-14: LGTM: Imported notFound helper correctly

Matches new not-found flow.


33-33: LGTM: Typed NotFoundRouteProps import

Type-only import is correct.


41-43: LGTM: MissingUserData shape

Clear and minimal payload type for the example.


487-487: LGTM: Route-level notFoundComponent registration

Correctly wires the per-route not-found UI.


602-613: LGTM: Discoverability link for not-found scenario

Nice UX touch for demonstrating the behavior.


661-673: LGTM: Throwing notFound with payload

Uses the helper as intended for missing user case.

Signed-off-by: leesb971204 <leesb971204@gmail.com>
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: 1

🧹 Nitpick comments (5)
packages/router-core/tests/load.test-d.ts (5)

6-8: Consider making data generic, not fixed unknown.

Issue #4744 calls for generic-aware typing so user payload type flows to the component. Today this asserts unknown | undefined. Prefer NotFoundRouteProps<TData = unknown> with data?: TData to preserve strong typing by defaulting to unknown but allowing concrete TData.

If you adopt a generic, extend tests like:

// Example additional check
type StringProps = NotFoundRouteProps<string>
expectTypeOf<StringProps['data']>().toEqualTypeOf<string | undefined>()

And in core types (outside this file), shape it as:

export type NotFoundRouteProps<TData = unknown> = {
  data?: TData
  isNotFound: true
  routeId: RouteIds<RegisteredRouter['routeTree']>
}

9-13: Type isNotFound as the literal true instead of boolean.

At runtime this is always true; a literal type improves narrowing and matches the issue’s expectations.

If you change the core type, update this test accordingly:

-expectTypeOf<NotFoundRouteProps['isNotFound']>().toEqualTypeOf<boolean>()
+expectTypeOf<NotFoundRouteProps['isNotFound']>().toEqualTypeOf<true>()

And similarly adjust later checks (e.g., Line 89) to expect true.


35-41: Prefer satisfies for stricter object conformance in tests.

Using satisfies preserves the object’s inferred shape while ensuring it conforms, catching excess props without widening.

Apply:

-const props: NotFoundRouteProps = {
+const props satisfies NotFoundRouteProps = {
   isNotFound: true,
   routeId: '/' as RouteIds<RegisteredRouter['routeTree']>,
 }

Repeat similarly for other object literals in this file.


43-50: Same satisfies suggestion for the “all properties” case.

-const props: NotFoundRouteProps = {
+const props satisfies NotFoundRouteProps = {
   data: { message: 'Not found' },
   isNotFound: true,
   routeId: '/' as RouteIds<RegisteredRouter['routeTree']>,
 }

1-1: Rename file for clarity.

Content focuses on not-found props, not “load”. Consider renaming to not-found-props.test.ts for discoverability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a29b905 and b2e4a0e.

📒 Files selected for processing (1)
  • packages/router-core/tests/load.test-d.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/router-core/tests/load.test-d.ts
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep framework-agnostic core router logic in packages/router-core/

Files:

  • packages/router-core/tests/load.test-d.ts
🔇 Additional comments (3)
packages/router-core/tests/load.test-d.ts (3)

23-33: LGTM: required-ness checks for isNotFound and routeId.

These assertions are good and align with the intended public surface.


84-98: Component-acceptance checks look correct.

These validations correctly ensure the component parameter matches the public NotFoundRouteProps surface.


110-126: Spreadability checks look good.

Props spreading and parameter inference assertions are appropriate and align with the intended usage.

Comment on lines +1 to +3
import { describe, expectTypeOf, test } from 'vitest'
import type { NotFoundRouteProps, RegisteredRouter, RouteIds } from '../src'

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

🧩 Analysis chain

Wrong test runner/file type: .test-d.ts with Vitest will not run (and .d.ts cannot contain runtime code).

This file imports/uses Vitest APIs but has a .d.ts suffix. By default, Vitest won’t pick up .test-d.ts, and .d.ts files are for ambient declarations, not executable code.

  • Fix: rename to .test.ts (recommended), or switch to tsd-runner and its assertions (then don’t import from vitest).

Run to verify current test matching and whether .test-d.ts is configured:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Vitest configs referencing .test-d.ts:"
rg -nP '(test|include).*d\.ts' -C2 -g 'vitest.config.*' || true

echo
echo "Existing .test-d.ts files:"
fd '\.test-d\.ts$' || true

echo
echo "Any tsd/tsd-runner usage:"
rg -nP "from 'tsd'|tsd-runner" -C2 || true

Length of output: 1004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Imports from 'vitest' (all files):"
rg -n "from\s+['\"]vitest['\"]" -S || true

echo
echo "2) .test-d.ts files that contain vitest/test/describe/expectTypeOf/expectType:"
fd '\.test-d\.ts$' -e || true | while read -r f; do
  if rg -n "(from\s+['\"]vitest['\"]|describe\s*\(|\btest\s*\(|expectTypeOf\b|expectType\b|it\s*\()" -n -S "$f" >/dev/null 2>&1; then
    echo "---- $f ----"
    rg -n "(from\s+['\"]vitest['\"]|describe\s*\(|\btest\s*\(|expectTypeOf\b|expectType\b|it\s*\()" -n -S "$f" || true
  fi
done

echo
echo "3) Head (first 20 lines) of .test-d.ts files that matched:"
fd '\.test-d\.ts$' -e || true | while read -r f; do
  if rg -n "(from\s+['\"]vitest['\"]|describe\s*\(|\btest\s*\(|expectTypeOf\b|expectType\b|it\s*\()" -n -S "$f" >/dev/null 2>&1; then
    echo "---- $f (head) ----"
    sed -n '1,20p' "$f" || true
  fi
done

echo
echo "4) Look for tsd / tsd-runner usage and references in package.json files:"
rg -n "from\s+['\"]tsd['\"]|tsd-runner" -S || true
rg -n '"(tsd|tsd-runner|vitest)"' -S **/package.json || true

echo
echo "5) Vitest config files / references:"
rg -n --hidden --glob '!node_modules' "vitest" -S || true
rg -n --hidden --glob '!node_modules' "vitest.config" -S || true

Length of output: 18513


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "packages/router-core/tests/load.test-d.ts"
  "packages/router-core/tests/utils.test-d.ts"
  "packages/router-core/tests/load.test.ts"
  "packages/start-client-core/src/tests/createServerFn.test-d.ts"
  "packages/arktype-adapter/tests/index.test-d.ts"
)

for f in "${files[@]}"; do
  if [ -f "$f" ]; then
    echo "---- $f ----"
    sed -n '1,240p' "$f" || true
    echo
  else
    echo "missing: $f"
  fi
done

echo "---- vitest config files (if any) ----"
rg -n --hidden --glob '!node_modules' "vitest.config" -S || true

Length of output: 22946


Rename .test-d.ts files that contain runtime Vitest code to .test.ts (or convert them to a type-only runner)

packages/router-core/tests/load.test-d.ts imports/uses Vitest runtime APIs (describe/test/expectTypeOf) but ends with .d.ts (a declaration file) and will not be executed by Vitest. Fix: rename to .test.ts (recommended) or convert to a type-only test runner (tsd/tsd-runner) and use type-only imports.

🤖 Prompt for AI Agents
In packages/router-core/tests/load.test-d.ts around lines 1 to 3, the file
imports and uses Vitest runtime APIs but is named as a TypeScript declaration
file (.d.ts) so Vitest won't run it; rename the file to load.test.ts so Vitest
executes it, or if you intend a type-only test convert to a type-only runner
(e.g., tsd/tsd-runner) and change imports to type-only to remove runtime usage.

@leesb971204
Copy link
Contributor Author

thanks for this PR! can you please

  • add some runtime tests for the various scenarios
  • add a typetest maybe? not sure if that makes sense / is possible
  • update docs
  • add usage to the kitchen sink example

When you have some time, could you take another look at this PR?
I believe I’ve added everything you mentioned.

@birkskyum birkskyum merged commit 6b235a7 into TanStack:main Nov 19, 2025
6 checks passed
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.

notFound data incorrectly typed as unknown and redundantly nested in data.data

4 participants