Repository navigation
Add prev/next nav to blog posts, reduce index gap - #859
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds centralized blog post data and on-site navigation: introduces Changes
Sequence Diagram(s)sequenceDiagram
participant Browser
participant BlogLayout
participant BlogPager
participant BlogPostsModule
Browser->>BlogLayout: Request /blog or /blog/{slug}
BlogLayout->>BlogPostsModule: import blogPosts
BlogLayout->>Browser: render children + <BlogCTA/>
alt route is /blog/{slug}
BlogLayout->>BlogPager: render Pager
BlogPager->>BlogPostsModule: read blogPosts
BlogPager->>BlogPager: find index for current slug
BlogPager->>Browser: render prev/next links (if present)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bcfa51786
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| (post) => `/blog/${post.slug}` === pathname | ||
| ); | ||
| const prev = index > 0 ? blogPosts[index - 1] : null; | ||
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; |
There was a problem hiding this comment.
Handle unmatched pathnames before deriving next post
findIndex returns -1 for routes that are not a specific post (for example /blog), but the current next expression treats -1 < blogPosts.length - 1 as true and sets next to blogPosts[0]. This causes the pager to render on non-post pages and can show an incorrect link whenever the pathname format does not exactly match (e.g., trailing-slash variants). Add an early index === -1 guard before computing prev/next so unmatched routes return null.
Useful? React with 👍 / 👎.
Greptile SummaryAdds prev/next navigation links to blog posts and refactors blog posts array into a shared module. The spacing on the blog index was tightened from
Confidence Score: 3/5
Important Files Changed
Last reviewed commit: 7bcfa51 |
| const index = blogPosts.findIndex( | ||
| (post) => `/blog/${post.slug}` === pathname | ||
| ); | ||
| const prev = index > 0 ? blogPosts[index - 1] : null; | ||
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | ||
|
|
||
| if (!prev && !next) return null; |
There was a problem hiding this comment.
pager shows incorrectly on index page when findIndex returns -1
| const index = blogPosts.findIndex( | |
| (post) => `/blog/${post.slug}` === pathname | |
| ); | |
| const prev = index > 0 ? blogPosts[index - 1] : null; | |
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | |
| if (!prev && !next) return null; | |
| const index = blogPosts.findIndex( | |
| (post) => `/blog/${post.slug}` === pathname | |
| ); | |
| if (index === -1) return null; | |
| const prev = index > 0 ? blogPosts[index - 1] : null; | |
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/app/components/blog-pager.tsx`:
- Around line 9-15: After computing index with blogPosts.findIndex((post) =>
`/blog/${post.slug}` === pathname) in blog-pager.tsx, guard the unmatched-path
case by returning early when index === -1 so you don't derive prev/next for
non-post routes; specifically, after computing index check "if (index === -1)
return null" before computing prev and next (which reference blogPosts[index -
1] and blogPosts[index + 1]).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b5789aec-88a8-42ac-9508-9199a067d630
📒 Files selected for processing (4)
web/app/blog/layout.tsxweb/app/blog/page.tsxweb/app/components/blog-pager.tsxweb/app/components/blog-posts.ts
| const index = blogPosts.findIndex( | ||
| (post) => `/blog/${post.slug}` === pathname | ||
| ); | ||
| const prev = index > 0 ? blogPosts[index - 1] : null; | ||
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | ||
|
|
||
| if (!prev && !next) return null; |
There was a problem hiding this comment.
Guard unmatched paths before deriving neighbors.
When findIndex returns -1 (e.g., on /blog), Line 13 resolves next to the first post, so the pager renders where it shouldn't. Add an early return for no-match routes.
💡 Suggested fix
export function BlogPager() {
const pathname = usePathname();
const index = blogPosts.findIndex(
(post) => `/blog/${post.slug}` === pathname
);
+ if (index === -1) return null;
+
const prev = index > 0 ? blogPosts[index - 1] : null;
const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null;
if (!prev && !next) return null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const index = blogPosts.findIndex( | |
| (post) => `/blog/${post.slug}` === pathname | |
| ); | |
| const prev = index > 0 ? blogPosts[index - 1] : null; | |
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | |
| if (!prev && !next) return null; | |
| export function BlogPager() { | |
| const pathname = usePathname(); | |
| const index = blogPosts.findIndex( | |
| (post) => `/blog/${post.slug}` === pathname | |
| ); | |
| if (index === -1) return null; | |
| const prev = index > 0 ? blogPosts[index - 1] : null; | |
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | |
| if (!prev && !next) return null; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/app/components/blog-pager.tsx` around lines 9 - 15, After computing index
with blogPosts.findIndex((post) => `/blog/${post.slug}` === pathname) in
blog-pager.tsx, guard the unmatched-path case by returning early when index ===
-1 so you don't derive prev/next for non-post routes; specifically, after
computing index check "if (index === -1) return null" before computing prev and
next (which reference blogPosts[index - 1] and blogPosts[index + 1]).
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/app/components/blog-pager.tsx">
<violation number="1" location="web/app/components/blog-pager.tsx:13">
P2: Guard against `findIndex` returning `-1`; otherwise a non-matching pathname incorrectly shows the first post as `next`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| (post) => `/blog/${post.slug}` === pathname | ||
| ); | ||
| const prev = index > 0 ? blogPosts[index - 1] : null; | ||
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; |
There was a problem hiding this comment.
P2: Guard against findIndex returning -1; otherwise a non-matching pathname incorrectly shows the first post as next.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/app/components/blog-pager.tsx, line 13:
<comment>Guard against `findIndex` returning `-1`; otherwise a non-matching pathname incorrectly shows the first post as `next`.</comment>
<file context>
@@ -0,0 +1,43 @@
+ (post) => `/blog/${post.slug}` === pathname
+ );
+ const prev = index > 0 ? blogPosts[index - 1] : null;
+ const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null;
+
+ if (!prev && !next) return null;
</file context>
| const next = index < blogPosts.length - 1 ? blogPosts[index + 1] : null; | |
| const next = index >= 0 && index < blogPosts.length - 1 ? blogPosts[index + 1] : null; |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/app/components/blog-cta.tsx`:
- Around line 13-14: The GitHubButton component currently hardcodes its
analytics location as "hero", causing misattribution; update the usage in
blog-cta.tsx to pass the same location prop as DownloadButton
(location="blog-bottom") and update the GitHubButton component
(github-button.tsx) to accept a location prop (e.g., prop name location) with a
sensible default ("hero") and use that prop when emitting analytics events in
the click handler (e.g., inside the function handling clicks), so blog-footer
clicks report "blog-bottom" while preserving existing behavior elsewhere.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 624d290b-c7ea-4cc9-ad75-f10093176b08
📒 Files selected for processing (3)
web/app/blog/layout.tsxweb/app/blog/show-hn-launch/page.tsxweb/app/components/blog-cta.tsx
💤 Files with no reviewable changes (1)
- web/app/blog/show-hn-launch/page.tsx
| <DownloadButton location="blog-bottom" /> | ||
| <GitHubButton /> |
There was a problem hiding this comment.
Fix GitHub CTA analytics location attribution.
DownloadButton is tagged with location="blog-bottom", but GitHubButton still tracks clicks as "hero" (hardcoded in web/app/components/github-button.tsx), so blog-footer clicks are misattributed.
Proposed fix
diff --git a/web/app/components/github-button.tsx b/web/app/components/github-button.tsx
@@
-export function GitHubButton() {
+export function GitHubButton({ location = "hero" }: { location?: string }) {
@@
- onClick={() => posthog.capture("cmuxterm_github_clicked", { location: "hero" })}
+ onClick={() => posthog.capture("cmuxterm_github_clicked", { location })}diff --git a/web/app/components/blog-cta.tsx b/web/app/components/blog-cta.tsx
@@
- <GitHubButton />
+ <GitHubButton location="blog-bottom" />📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <DownloadButton location="blog-bottom" /> | |
| <GitHubButton /> | |
| <DownloadButton location="blog-bottom" /> | |
| <GitHubButton location="blog-bottom" /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/app/components/blog-cta.tsx` around lines 13 - 14, The GitHubButton
component currently hardcodes its analytics location as "hero", causing
misattribution; update the usage in blog-cta.tsx to pass the same location prop
as DownloadButton (location="blog-bottom") and update the GitHubButton component
(github-button.tsx) to accept a location prop (e.g., prop name location) with a
sensible default ("hero") and use that prop when emitting analytics events in
the click handler (e.g., inside the function handling clicks), so blog-footer
clicks report "blog-bottom" while preserving existing behavior elsewhere.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d7f41915ce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (pathname === "/blog") return null; | ||
|
|
||
| const slug = pathname.replace("/blog/", ""); |
There was a problem hiding this comment.
Normalize blog index pathname before showing CTA
The index guard only checks "/blog", so visiting the valid trailing-slash variant (/blog/) falls through and renders the CTA with location set to "blog-". Because this app has skipTrailingSlashRedirect: true in web/next.config.ts, that URL is not automatically canonicalized, so users can hit this path and see post-only CTA controls on the index while analytics events are mislabeled. Treat both /blog and /blog/ (or normalize pathname) before deriving the slug.
Useful? React with 👍 / 👎.
* Add prev/next navigation to blog posts, reduce index gap * Add download/GitHub CTA to all blog posts via layout * Track blog post slug in PostHog download/GitHub click events
Summary
blogPostsarray intoweb/app/components/blog-posts.tsspace-y-8tospace-y-4Testing
/blogshould have tighter spacing between entriesSummary by cubic
Adds prev/next navigation and a Download/GitHub CTA to all blog posts, with PostHog click events including the post slug. Tightens blog index spacing for a cleaner reading flow.
New Features
Refactors
Written for commit d7f4191. Summary will update on new commits.
Summary by CodeRabbit