Skip to content

Comments

Add default 404 page and loading state to project example#1257

Merged
KyleAMathews merged 4 commits intomainfrom
claude/fix-issue-889-DuTLH
Feb 18, 2026
Merged

Add default 404 page and loading state to project example#1257
KyleAMathews merged 4 commits intomainfrom
claude/fix-issue-889-DuTLH

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Feb 17, 2026

Fixes the React projects example which was broken on initial sign-up/sign-in due to multiple issues: a crash from accessing undefined project data, missing 404 handling, a broken auth API route, and a race condition in default project creation.

Root Causes

1. Auth API route didn't handle sub-paths — The route at api/auth.ts only matched /api/auth exactly. Better Auth needs to handle sub-paths like /api/auth/sign-up/email, causing all auth requests to 404. Fixed by moving to a catch-all route at api/auth/$.ts (matching the existing tRPC pattern).

2. Default project creation via useEffect caused race conditions — The _authenticated layout created a default project in a useEffect, then index.tsx redirected to it in another useEffect. This meant components rendered before data was ready, causing crashes on project.name when useLiveQuery hadn't resolved yet.

3. No defaultNotFoundComponent — TanStack Router threw an unhandled error when encountering a not-found route with no fallback component configured.

Approach

Move all data preparation into beforeLoad — Auth check, collection preload, default project creation, and redirect all happen in beforeLoad before any component renders. The insert waits for tx.isPersisted.promise to get the server-assigned ID before redirecting, avoiding the stale-random-ID problem.

Validate project existence in the loader — The $projectId route loader checks projectCollection.has(projectId) after preload and throws notFound() for invalid/missing projects.

Add reusable NotFound component — Extracted to src/components/NotFound.tsx using <Link> for client-side navigation, consistent with the todo and offline-transactions examples.

Key Invariants

  • After preload() resolves, collection.has(key) and collection.toArray reflect both synced and optimistic state
  • beforeLoad runs sequentially (parent → child) before any component renders — no timing races
  • await tx.isPersisted.promise ensures the server-assigned ID is used for redirect, not the temporary random ID

Non-goals

  • Refactoring the random ID generation pattern — that's a broader concern across the example
  • Adding error/loading state handling to individual useLiveQuery calls — the loader validation makes this unnecessary

Files changed

File Change
src/routes/api/auth.tssrc/routes/api/auth/$.ts Catch-all route for Better Auth sub-paths
src/lib/auth.ts Add baseURL config and port 5174 to trusted origins
src/routes/_authenticated.tsx Remove useEffect default project creation
src/routes/_authenticated/index.tsx Move project creation + redirect into beforeLoad
src/routes/_authenticated/project/$projectId.tsx Validate project in loader with notFound()
src/components/NotFound.tsx New reusable 404 component
src/router.tsx Register defaultNotFoundComponent

Release Impact

Example-only change — no impact on published packages.


Fixes #889

Add a guard in the ProjectPage component to handle the case where the
project or user membership data hasn't loaded yet, preventing the
"Cannot read properties of undefined (reading 'name')" crash. Also add
a defaultNotFoundComponent to the router to handle invalid routes
gracefully instead of throwing an unconfigured notFoundError.

Fixes #889

https://claude.ai/code/session_01QJP1o2CKx7s9iLPNEkwcuP
@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

⚠️ No Changeset found

Latest commit: 774d282

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 17, 2026

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1257

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1257

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1257

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1257

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1257

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1257

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1257

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1257

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1257

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1257

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1257

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1257

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1257

commit: 774d282

@github-actions
Copy link
Contributor

github-actions bot commented Feb 17, 2026

Size Change: 0 B

Total Size: 92.1 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.22 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.75 kB
./packages/db/dist/esm/collection/mutations.js 2.34 kB
./packages/db/dist/esm/collection/state.js 3.49 kB
./packages/db/dist/esm/collection/subscription.js 3.71 kB
./packages/db/dist/esm/collection/sync.js 2.41 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.7 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.69 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 2.17 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 538 B
./packages/db/dist/esm/local-only.js 808 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 4.09 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.43 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.81 kB
./packages/db/dist/esm/query/compiler/index.js 2.02 kB
./packages/db/dist/esm/query/compiler/joins.js 2.11 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.06 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.44 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 2.42 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.62 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.3 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 924 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 952 B
./packages/db/dist/esm/utils/cursor.js 457 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Feb 17, 2026

Size Change: 0 B

Total Size: 3.7 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.34 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

…omponent

Throw notFound() from the route loader when the project ID is invalid
or doesn't exist, instead of showing a misleading "Loading..." message.
Extract NotFound into a reusable component using Link for client-side
navigation, consistent with the todo and offline-transactions examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@kevin-dp kevin-dp left a comment

Choose a reason for hiding this comment

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

After the _authenticated layout creates a default project with a random ID, the index route redirects to /project/. If the project data wasn't yet synced, projects[0] was undefined and the component crashed on project.name.

Since now we detect this and show a not found component, this means that we will see this NotFound page on initial login (since i would reliably get the error, so i will now reliably get this not found page). Will it automatically redirect to the project when it syncs in? Or shouldn't we wait with the redirect to the project until we are sure it synced?

EDIT: Claude's take:

The reviewer's point is that this trades a crash for a potentially confusing UX: on initial login, the user would now reliably see a "Page Not Found" page instead of a crash. The reviewer is asking:

  1. Will it auto-recover once the sync completes? (Probably not — the loader already ran and threw notFound(), so the user is stuck on the 404 page unless they navigate away.)
  2. Shouldn't the redirect itself be deferred until the data is confirmed to be synced, rather than redirecting eagerly and then showing a 404?

This is a legitimate architectural concern. The fix addresses the symptom (crash on undefined) but not the root cause (redirecting to a project before its data is available). A better approach might be:

  • Show a loading state while waiting for sync to complete
  • Only redirect to the project once it's confirmed to exist in the collection
  • Or have the $projectId route show a loading/pending state instead of 404 when the project isn't found yet but sync is still in progress

KyleAMathews and others added 2 commits February 18, 2026 09:51
- Move auth API route to catch-all (api/auth/$.ts) so Better Auth
  sub-paths like /api/auth/sign-up/email are handled correctly
- Add baseURL and port 5174 to Better Auth trusted origins
- Move default project creation from useEffect into beforeLoad so
  data is ready before first render, eliminating race conditions
- Wait for project to persist before redirecting to get server-assigned ID
- Remove unused useEffect and isLoading from authenticated layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@KyleAMathews KyleAMathews merged commit 1f20d6e into main Feb 18, 2026
7 checks passed
@KyleAMathews KyleAMathews deleted the claude/fix-issue-889-DuTLH branch February 18, 2026 16:57
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.

Examples: React 'projects' NotFound Error

3 participants