Skip to content

fix(solid-router): hydrate route error components consistently - #8438

Open
dvd233 wants to merge 1 commit into
TanStack:solid-router-v2-prefrom
dvd233:fix/solid-error-component-hydration-8241
Open

dvd233 wants to merge 1 commit into
TanStack:solid-router-v2-prefrom
dvd233:fix/solid-error-component-hydration-8241

Conversation

@dvd233

@dvd233 dvd233 commented Sep 15, 2026

Copy link
Copy Markdown

What

  • render route match errors from a route-specific boundary position on both the server and client
  • keep render-time descendant errors handled by the existing CatchBoundary
  • remount the route error UI when a subsequent loader failure supplies a new error
  • add unit and Solid Start browser regressions for repeat failures, hydration warnings, and interactive controls

Why

During SSR, MatchInner rendered a route errorComponent inline, while the client threw the same error into CatchBoundary. Solid assigns hydration IDs from the owner tree, so the client could not claim the server-rendered error UI and its controls remained unwired.

Rendering known route errors from one stable boundary position keeps the server and client owner trees aligned.

Fixes #8241

Tests

  • cd packages/solid-router && pnpm run test:unit (873 passed, 2 skipped; server mode: 7 passed)
  • cd packages/solid-router && pnpm run test:types (TypeScript 5.6 through 7.0)
  • cd packages/solid-router && pnpm run test:eslint (0 errors; 33 existing warnings)
  • cd packages/solid-router && pnpm run build
  • cd e2e/solid-start/query-integration && pnpm run build
  • Playwright Chromium: error-component-hydration.spec.ts and not-found-reload.spec.ts (4 passed)

Checklist

  • I have followed the steps in the contributing guide.
  • I have tested the code changes locally.
  • I fully understand the code in this pull request, including the AI-assisted portions I reviewed and rewrote.
  • This change affects published code, and I have added a changeset.

@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c52da77a-3677-4e87-ab52-f05bf4adc181

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@brenelz

brenelz commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Review

Intent: Fix #8241 — SSR rendered route errorComponent inside MatchInner, client threw into CatchBoundary/Errored, so hydration IDs didn’t match and controls stayed dead. RouteCatchBoundary renders known match errors at the same boundary position on both sides; render-time descendant errors still go through CatchBoundary.

Verdict: Approach looks right and matches the bug. No hard blockers from the diff; I’d want maintainer CI (or a green run on solid-router-v2-pre) before merge.

Blockers

None in the code itself.

Should confirm

  1. Thin CI on this base. Checks here are basically Socket/CodeRabbit; author cites local unit/types/e2e. Branch is solid-router-v2-pre, not default — worth a maintainer re-run or explicit ACK that local green is enough.
  2. getResetKey is unused while hasError is true. Loader-error UI lives outside CatchBoundary, so the existing reset-key effect won’t fire until the match leaves error. New errors remount via createEffect(props.getError) + Object.is — covered by the invalidate unit test. Confirm that’s intentional for “same error ref, updatedAt bump only” (rare).

Nits

  • MatchInner still throws on status === 'error' — dead for the loader path once RouteCatchBoundary owns it; fine as a backstop for the CatchBoundary child path, or delete later for clarity.
  • SSR no longer passes info={{ componentStack: '' }} (old inline path did). Only matters if something read info during SSR; default ErrorComponent doesn’t.
  • Loader reset() is still remount-only (setRetryKey), not a route retry — same practical limit as before for sticky match errors; invalidate/navigation remains the real recovery. Fine, just don’t document it as “retry loader.”
  • E2E falsy-error case (?falsy=true → message "undefined") is a good edge pin; a bit odd as product UX but OK as a regression.

What’s strong

  • Clear root cause; shared renderErrorComponent helper.
  • Unit test for second loader failure after invalidate + onCatch counts.
  • Playwright checks hydration warning strings and that the button actually toggles data-clicked.
  • Changeset for @tanstack/solid-router.

@nx-cloud

nx-cloud Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 75a7ac8

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

☁️ Nx Cloud last updated this comment at 2026-09-16 12:25:19 UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 16, 2026

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@8438

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@8438

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@8438

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@8438

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@8438

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@8438

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@8438

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@8438

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@8438

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@8438

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@8438

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@8438

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@8438

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@8438

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@8438

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@8438

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@8438

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@8438

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@8438

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@8438

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@8438

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@8438

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@8438

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@8438

commit: 75a7ac8

@codspeed

codspeed Bot commented Sep 16, 2026

Copy link
Copy Markdown

Hooray! CodSpeed harness just leveled up!

The base and head of this comparison were measured with different runner settings, so their benchmark values are not directly comparable.

What changed between base and head:

Re-run the base with the same settings to get a valid performance comparison.


Comparing dvd233:fix/solid-error-component-hydration-8241 (75a7ac8) with solid-router-v2-pre (67a9040)1

Open in CodSpeed

Footnotes

  1. No successful run was found on solid-router-v2-pre (9de3573) during the generation of this report, so 67a9040 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

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.

2 participants