feat: defer member/team registries on detail routes (#156)#185
Merged
Conversation
loadMemberRegistry now resolves outside the loader's await, returned as a Promise. BlogArticle wraps the author footer in <Suspense>+<Await> so primary content (h1, body, toc) paints without waiting for the member registry. Targets INP improvement per #156.
loadMemberRegistry resolves outside the loader's await for the jobs detail route, returned as a Promise. The HiringContact footer block is now wrapped in <Suspense>+<Await>. JSON-LD stays on the critical path so crawlers receive the JobPosting schema on first paint.
loadMemberRegistry resolves outside the loader's await for client story pages, returned as a Promise. StoryArticle wraps StoryTeamBlock (footer) in <Suspense>+<Await>. resolvedTools stays on the critical path because StoryMetas renders tool icons in the above-the-fold sidebar. Adds loader test coverage for the route — 4 cases: missing slug, fetch failure, sync primaries, deferred team.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
H1 — restore loader concurrency: fire loadMemberRegistry() before
awaiting fetchBlogpost/fetchJob/fetchStory so the registry call no
longer serialises behind the primary fetch. Preserves the parallelism
of the original Promise.all without re-introducing the critical-path
await.
H2 — add errorElement={null} to all three <Await> boundaries
(blog-article, jobs route, story-article). Prevents a future-throwing
registry resolver from escalating to the root ErrorBoundary after
streaming has already started.
Contributor
Author
|
Code review feedback addressed in c3ef9c8. H1 (blocking) — loader concurrency restored. Fire H2 (blocking) — Not addressed (low priority, can be follow-ups):
All 414 tests passing locally ( |
wab
added a commit
that referenced
this pull request
May 15, 2026
Captured via scripts/perf/baseline-run.sh against prod URLs. Same 6 routes as the 2026-05-14 baseline. Single-run lab data — high variance across re-runs (±30-50% on LCP), so not a credible regression signal on its own. Kept for trend reference once we accumulate medians or shipped PRs to compare against (e.g. #156 deferred footers landed between 2026-05-14 and now).
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #156.
Summary
Move non-critical loader work off the blocking await on the three detail routes (blog, jobs, clients). Primary content (
<h1>, body, hero, sidebar) paints first; member registry resolution streams in afterwards via<Suspense>+<Await>.Per-route scope
blog/$slug($lang)/jobs/$slug($lang)/clients/$slugChanges
app/routes/_main.blog.$slug.tsx—loadMemberRegistry()no longer awaited;authorreturned asPromise<Member | null>.app/components/blog/blog-article.tsx— author footer wrapped in<Suspense fallback={null}><Await>.app/routes/_main.($lang).jobs.$slug.tsx— same pattern forcontact.ld(JSON-LD) stays sync.app/routes/_main.($lang).clients.$slug.tsx—loadMemberRegistry()deferred forresolvedTeam.loadToolRegistry()stays sync (sidebar metas).app/components/stories/story-article.tsx—StoryTeamBlockwrapped in<Suspense>.Tests
app/routes/_main.blog.$slug.test.ts— happy-path split into "primaries sync" + "author is a Promise resolving to expected value".app/routes/_main.($lang).jobs.$slug.test.ts— new test assertingcontactis a Promise;ldstays sync.app/routes/_main.($lang).clients.$slug.test.ts— new file, 4 cases: 404 (no slug), 404 (fetch failure), sync primaries, deferred team.All 414 tests pass (
pnpm test:run). Typecheck + Biome clean.Measurement plan
Single-run lab PSI is too noisy to credit a defer-pattern win (we showed ±30-50 % LCP variance across consecutive runs on this site). The real signal lives in Vercel Speed Insights p75 INP / TBT once production traffic accumulates.
docs/project/performance-baseline.mdChangelog with delta vs 2026-05-13 site-wide RUMTest plan
pnpm typecheck && pnpm check && pnpm test:runall green locally