Skip to content

Conversation

@natew
Copy link
Collaborator

@natew natew commented Jan 23, 2026

Summary

Research and implementation plan for allowing layouts to access loader data on SSR.

Proposed Features

  1. Layout Loaders - Allow _layout.tsx files to export a loader() function
  2. useMatches() Hook - Access all matched routes' loader data from any component

Use Case

DocsQuickNav in tamagui.dev layout needs frontmatter.headings from the page's loader to render statically (avoid hydration flash).

Key Findings from Research

  • route.layouts is already tracked in getServerManifest.ts:49,73
  • cli/build.ts:273 skips _layout files - needs to include them for server builds
  • fileSystemRouterPlugin.tsx:84-88 only runs page loader - needs parallel layout execution
  • TanStack Router and Remix both support loaders on all routes (including layouts)

Implementation Phases

Phase Goal
0 Build system changes - include layout server bundles
1 Dev server layout loader support
2 Production server layout loader support
3 ServerContext enhancement
4 useMatches hook
5 (Optional) beforeLoad for auth/context

Client-Side Note

One intentionally doesn't run loaders on client-side navigation. This is preferred and will remain unchanged. useMatches() on client will return cached/hydrated data from SSR.

See plans/loader-overhaul.md for full technical details.

Test Plan

  • Review plan completeness
  • Validate assumptions about build system
  • No implementation in this PR - plan only

Deep research and implementation plan for:
1. Layout loaders - allow _layout.tsx to export loader()
2. useMatches() hook - access all matched routes' loader data

Key findings:
- route.layouts already tracked in getServerManifest.ts
- cli/build.ts:273 skips _layout files (need to fix)
- fileSystemRouterPlugin.tsx only runs page loader (need to fix)
@railway-app
Copy link

railway-app bot commented Jan 23, 2026

🚅 Deployed to the one-pr-656 environment in onestack.dev

Service Status Web Updated (UTC)
one ✅ Success (View Logs) Web Jan 24, 2026 at 2:29 am

@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 23, 2026 22:05 Destroyed
- Add useMatches() hook to access all matched routes' loader data
- Add useMatch(routeId) to find a specific match by route ID
- Add usePageMatch() to get the current page's match
- Support layout loaders - _layout.tsx can export loader() functions
- Run layout loaders in parallel on both dev and production servers
- Add build system support for layout server paths
- Add One.RouteMatch type for type safety
- Add tests for useMatches functionality
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 23, 2026 22:14 Destroyed
natew added 2 commits January 23, 2026 12:17
- Pass matches array to render function in dev server
- Add tests for dynamic params, catch-all params
- Add tests for complex loaderData scenarios
- Add tests for finding matches by routeId
Document that layout loader data is cached from SSR on client-side
navigation since One intentionally doesn't re-run loaders on client.
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 23, 2026 22:18 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 23, 2026 22:19 Destroyed
- Add buildClientMatches() to construct matches array after navigation
- Add initClientMatches() to initialize from server context on hydration
- Call setClientMatches() in linkTo after preload completes
- Add e2e tests for useMatches with client navigation
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 23, 2026 22:26 Destroyed
natew added 6 commits January 23, 2026 12:57
- Fix layout loaders not running for SSG routes in production
- Add layout server path tracking during build for SSG routes
- Preserve minimal layout info in buildInfo.json for loader execution
- Simplify buildClientMatches to preserve layout matches on navigation
- Fix useMatches always returning client store on client (not hydrated data)
- Fix contextKey format mismatch in layout server paths (./path vs /path)
When route.file or serverPath already contains dist/server (from
loaderServerPath), don't prepend it again. This fixes SSR loader
redirects in production builds.
Tests 3+ levels of nested layouts with loaders to verify:
- 4 matches total (root layout, level1 layout, level2 layout, page)
- Each layout can access its own loader data via useMatch
- Page can access all layout loader data via useMatches
- Add test for useMatches with dynamic routes (params in matches)
- Add error handling test setup (currently skipped)
- Add error handling for loader failures in dev server
- Update plan document with completed items
Avoids conflicts with common dev servers like 8081
- Add hooks-test page to test useMatch and usePageMatch hooks
- Test useMatch with valid routeId finds the match
- Test useMatch with invalid routeId returns undefined
- Test usePageMatch returns current page match
- Test matches are consistent after hydration
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:00 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:03 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:10 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:25 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:34 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:38 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:38 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:46 Destroyed
@natew natew force-pushed the feat/loader-overhaul-plan branch from 9ae645b to 7b7076f Compare January 24, 2026 00:50
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 00:50 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 01:21 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 01:45 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 01:48 Destroyed
@railway-app railway-app bot temporarily deployed to onestack.dev / one-pr-656 January 24, 2026 02:25 Destroyed
@natew natew marked this pull request as ready for review January 24, 2026 02:43
@natew natew added this pull request to the merge queue Jan 24, 2026
Merged via the queue into main with commit afa51c3 Jan 24, 2026
9 checks passed
@natew natew deleted the feat/loader-overhaul-plan branch January 24, 2026 03:04
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