Skip to content

perf: skip RecentPostsWidget fetch when its rail is hidden on mobile#1024

Merged
LadyBluenotes merged 3 commits into
mainfrom
perf/lazy-recent-posts-widget
Jul 2, 2026
Merged

perf: skip RecentPostsWidget fetch when its rail is hidden on mobile#1024
LadyBluenotes merged 3 commits into
mainfrom
perf/lazy-recent-posts-widget

Conversation

@LadyBluenotes

@LadyBluenotes LadyBluenotes commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

RecentPostsWidget (the "Latest Posts" box in the right rail) is mounted unconditionally on every docs page (LibraryLayout.tsx) and every blog post page (blog.$.tsx), with no posts prop passed in either case. That means it always fires its own useQuery fetch on mount.

The rail it lives in is hidden below the md breakpoint via CSS only (hidden md:blockdisplay:none, not a conditional unmount). So on mobile, the component was still mounting and still firing the fetch even though the box was never visible to the user.

This fixes that by threading the same breakpoint the CSS already uses into the query's enabled flag, so the fetch itself is skipped on mobile instead of just being hidden after the fact.

Changes

  • src/utils/useMediaQuery.ts (new) — hoisted out of LibraryLayout.tsx where it already existed as a private helper, so blog.$.tsx can reuse it too. No behavior change to the existing call site.
  • src/components/RecentPostsWidget.tsx — new enabled?: boolean prop (default true), threaded into the query as enabled: posts === undefined && enabled. blog.index.tsx already passes posts explicitly and is unaffected either way.
  • src/components/LibraryLayout.tsx — passes enabled={!shouldShowDocsPartnerSlot}, reusing the existing shouldShowDocsPartnerSlot media query (max-width: 767.98px) already computed in this file for a different mobile-only element, instead of adding a second matchMedia listener.
  • src/routes/blog.$.tsx — passes enabled={isDesktopViewport} via a new useMediaQuery('(min-width: 768px)') call (no existing media query call here to reuse).

Why this is safe

  • Desktop is unaffected. The component still mounts and still shows its skeleton immediately (unconditional mount, same as today) — only the query's enabled flag changes, and it evaluates true on desktop just like before.
  • No hydration mismatch. useMediaQuery defaults to false via useState, deterministically, on both server and the first client render — server and client agree before the media query listener ever runs, so there's nothing for React to warn about.
  • No double-fetch on the desktop→mobile/mobile→desktop transition. Verified against the actual @tanstack/react-query v5.100.11 source: enabled: false → true triggers exactly one fetch, no re-fetch loop.
  • Reviewed by critic and rubber-duck before implementation — no blocking issues from either. critic suggested reusing the existing shouldShowDocsPartnerSlot value instead of adding a second listener in LibraryLayout.tsx, which is what's implemented. rubber-duck caught that blog.$.tsx needed the same fix as LibraryLayout.tsx (an earlier plan draft had missed it).

Scope note

This does not touch the request itself being a client-fetch-on-mount rather than SSR-loaded/prefetched data — that's a separate, larger question and out of scope here. This PR only stops the fetch from firing when its result can never be seen.

Summary by CodeRabbit

  • New Features
    • Added responsive gating for the Recent Posts widget so it only loads in the blog/docs sidebars when appropriate for the current viewport and layout.
  • Bug Fixes
    • Prevented unnecessary recent-posts fetching when recent posts aren’t needed (e.g., when alternate docs content is shown or on smaller screens), reducing wasted requests and improving perceived load consistency.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
tanstack-com eefd557 Commit Preview URL

Branch Preview URL
Jul 02 2026, 05:37 AM

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@LadyBluenotes, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bdbcd155-415c-42de-9db9-497501e60ef8

📥 Commits

Reviewing files that changed from the base of the PR and between 796f222 and eefd557.

📒 Files selected for processing (1)
  • src/components/LibraryLayout.tsx
📝 Walkthrough

Walkthrough

A shared useMediaQuery hook is extracted, RecentPostsWidget gains an enabled prop to skip fetching, and LibraryLayout plus the blog route now pass viewport- and slot-based enable flags into the widget.

Changes

Conditional RecentPostsWidget fetching

Layer / File(s) Summary
Shared useMediaQuery hook
src/utils/useMediaQuery.ts
New hook uses window.matchMedia and a change event listener to track and return media query match state.
RecentPostsWidget enabled prop
src/components/RecentPostsWidget.tsx
Adds optional enabled prop (default true); fetch runs only when posts is undefined and enabled is true.
LibraryLayout wiring
src/components/LibraryLayout.tsx
Removes the local useMediaQuery implementation, imports the shared hook, and passes enabled={!shouldShowDocsPartnerSlot} to RecentPostsWidget.
Blog route wiring
src/routes/blog.$.tsx
Imports useMediaQuery, computes isDesktopViewport from a (min-width: 768px) query, and passes it as enabled to RecentPostsWidget.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: skipping RecentPostsWidget fetching when the rail is hidden on mobile.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/lazy-recent-posts-widget

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/LibraryLayout.tsx (1)

886-886: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Use a desktop query for the query gate useMediaQuery('(max-width: 767.98px)') starts as false, so enabled={!shouldShowDocsPartnerSlot} is true on the first render and RecentPostsWidget can still fetch on mobile before the media query flips. Match the blog route and gate this with a positive desktop query instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/LibraryLayout.tsx` at line 886, The query gate in
LibraryLayout’s docs partner/recent posts logic is using a mobile max-width
check that starts false, so RecentPostsWidget can fetch on the first mobile
render before the media query updates. Change shouldShowDocsPartnerSlot to use a
positive desktop query in LibraryLayout so enabled tracks desktop immediately,
and keep the gating tied to that desktop-aware boolean.
🧹 Nitpick comments (1)
src/routes/blog.$.tsx (1)

12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a shared breakpoint constant instead of duplicated literal strings.

'(min-width: 768px)' here and '(max-width: 767.98px)' in LibraryLayout.tsx both hand-encode the Tailwind md breakpoint (also referenced via RightRail breakpoint="md" and hidden md:block). Centralizing this in one constant would reduce drift risk if the breakpoint ever changes.

Also applies to: 78-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/blog`.$.tsx at line 12, The responsive breakpoint is duplicated as
literal media-query strings, which can drift from the Tailwind md breakpoint
used elsewhere. Introduce and reuse a shared breakpoint constant in the
components using useMediaQuery and LibraryLayout, and align RightRail
breakpoint="md" and the md-based visibility classes to that same source so the
breakpoint definition lives in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/LibraryLayout.tsx`:
- Line 886: The query gate in LibraryLayout’s docs partner/recent posts logic is
using a mobile max-width check that starts false, so RecentPostsWidget can fetch
on the first mobile render before the media query updates. Change
shouldShowDocsPartnerSlot to use a positive desktop query in LibraryLayout so
enabled tracks desktop immediately, and keep the gating tied to that
desktop-aware boolean.

---

Nitpick comments:
In `@src/routes/blog`.$.tsx:
- Line 12: The responsive breakpoint is duplicated as literal media-query
strings, which can drift from the Tailwind md breakpoint used elsewhere.
Introduce and reuse a shared breakpoint constant in the components using
useMediaQuery and LibraryLayout, and align RightRail breakpoint="md" and the
md-based visibility classes to that same source so the breakpoint definition
lives in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd79e5f9-1bf4-4012-ab4a-02a739e82c85

📥 Commits

Reviewing files that changed from the base of the PR and between 616b229 and 4c27298.

📒 Files selected for processing (4)
  • src/components/LibraryLayout.tsx
  • src/components/RecentPostsWidget.tsx
  • src/routes/blog.$.tsx
  • src/utils/useMediaQuery.ts

@LadyBluenotes LadyBluenotes merged commit 8e8af15 into main Jul 2, 2026
7 checks passed
@LadyBluenotes LadyBluenotes deleted the perf/lazy-recent-posts-widget branch July 2, 2026 15:16
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