Skip to content

feat(marketing): add related posts section to blog post pages#1147

Merged
saddlepaddle merged 1 commit into
mainfrom
add-related-posts-section-to-blog-post-pages
Feb 3, 2026
Merged

feat(marketing): add related posts section to blog post pages#1147
saddlepaddle merged 1 commit into
mainfrom
add-related-posts-section-to-blog-post-pages

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Feb 2, 2026

Summary

  • Add a "Related Posts" section to blog post pages, rendered above the footer using existing BlogCard components
  • Posts specify related posts via a relatedSlugs frontmatter array; falls back to the 3 most recent other posts when not specified
  • Blog cards now use equal-height layout with clamped descriptions and auto-fit grid to fill available width

Test plan

  • Run bun dev --filter=@superset/marketing and visit each blog post
  • Confirm specified related posts appear in the correct order
  • Remove relatedSlugs from one post's frontmatter and confirm fallback shows other posts
  • Verify cards fill the full width and have equal heights

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a "Related Posts" section to blog articles, displaying curated related content below the main post. This helps readers discover additional relevant articles and improves content discoverability.

Add a "Related Posts" section above the footer on blog post pages.
Posts can specify related posts via `relatedSlugs` frontmatter field,
falling back to the 3 most recent other posts when not specified.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 2, 2026

📝 Walkthrough

Walkthrough

This pull request adds a related posts feature to the blog system. Three blog post files receive relatedSlugs frontmatter entries, the BlogPost type gains an optional relatedSlugs field, and new utilities enable fetching and displaying related posts. Components are updated to render related posts via enhanced layout and card components.

Changes

Cohort / File(s) Summary
Blog Content Files
apps/marketing/content/blog/git-worktrees-history-deep-dive.mdx, how-to-get-hit.mdx, terminal-daemon-deep-dive.mdx
Added relatedSlugs frontmatter metadata with cross-references between posts; minor trailing whitespace cleanup in one file.
Type System
apps/marketing/src/lib/blog-utils.ts
Extended BlogPost interface with optional relatedSlugs?: string[] field to support related post references.
Blog Utilities
apps/marketing/src/lib/blog.ts
Introduced getRelatedPosts() function that retrieves related BlogPost entries either from explicit relatedSlugs or falls back to first three posts (excluding current); populates relatedSlugs from post frontmatter.
Page & Layout Components
apps/marketing/src/app/blog/[slug]/page.tsx, components/BlogPostLayout/BlogPostLayout.tsx
Updated page component to fetch and pass relatedPosts prop; enhanced BlogPostLayout to accept and render a new Related Posts section using BlogCard components.
BlogCard Component
apps/marketing/src/app/blog/components/BlogCard/BlogCard.tsx
Adjusted layout styling with full height container, line clamping for descriptions, and extended AuthorAvatar usage with twitterHandle and size props.

Sequence Diagram(s)

sequenceDiagram
    participant Page as Blog Page<br/>[slug]
    participant BlogUtil as Blog Utilities<br/>(blog.ts)
    participant Layout as BlogPostLayout<br/>Component
    participant Card as BlogCard<br/>Component
    participant Data as BlogPost<br/>Data

    Page->>BlogUtil: getRelatedPosts({ slug, relatedSlugs })
    BlogUtil->>Data: filter posts matching relatedSlugs<br/>or get first 3 posts
    Data-->>BlogUtil: return related BlogPost[]
    BlogUtil-->>Page: relatedPosts
    Page->>Layout: pass post, relatedPosts,<br/>children
    Layout->>Layout: check relatedPosts<br/>is non-empty
    alt relatedPosts exists
        Layout->>Card: map & render BlogCard<br/>for each related post
        Card-->>Layout: rendered card
    end
    Layout-->>Page: rendered layout<br/>with related section
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 With whiskers twitching, I hop with glee,
Related posts now linked, three by three!
Cards display with tweets and author names,
Blog readers jumping through content lanes!
No workaround needed, just pure delight,
Connected stories, shining bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a related posts section to blog post pages, which aligns with all file modifications.
Description check ✅ Passed The description covers the main feature, fallback behavior, and visual changes, with a test plan, but lacks formal sections from the template (Related Issues, Type of Change, Testing, Screenshots).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-related-posts-section-to-blog-post-pages

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
apps/marketing/src/lib/blog.ts (2)

32-42: ⚠️ Potential issue | 🟠 Major

Normalize relatedSlugs to a string array before storing.

If frontmatter provides a single string (or non-array), getRelatedPosts will throw when calling .map().

🛡️ Proposed normalization
 		return {
 			slug,
 			url: `/blog/${slug}`,
 			title: data.title ?? "Untitled",
 			description: data.description,
 			author: data.author ?? "Unknown",
 			date: dateValue,
 			category: data.category ?? "News",
 			image: data.image,
-			relatedSlugs: data.relatedSlugs,
+			relatedSlugs: Array.isArray(data.relatedSlugs)
+				? data.relatedSlugs.filter(
+						(slug): slug is string =>
+							typeof slug === "string" && slug.length > 0
+				  )
+				: undefined,
 			content,
 		};

44-45: ⚠️ Potential issue | 🟠 Major

Don’t swallow frontmatter parse errors; log with context.

Silent failures make missing posts hard to diagnose.

🧾 Suggested logging
-	} catch {
-		return null;
-	}
+	} catch (error) {
+		console.error("[blog/parseFrontmatter] Failed to parse blog post", {
+			filePath,
+			error,
+		});
+		return null;
+	}
🧹 Nitpick comments (3)
apps/marketing/src/lib/blog-utils.ts (1)

14-24: Document why relatedSlugs is optional.

Guideline calls for documenting optional interface fields.

♻️ Proposed doc addition
 export interface BlogPost {
 	slug: string;
 	url: string;
 	title: string;
 	description?: string;
 	author: string;
 	date: string;
 	category: BlogCategory;
 	image?: string;
+	/**
+	 * Optional frontmatter list of related post slugs.
+	 * When omitted, related posts fall back to recent posts.
+	 */
 	relatedSlugs?: string[];
 	content: string;
 }
apps/marketing/src/lib/blog.ts (1)

97-100: Filter out the current slug in explicit related lists.

Prevents self-links when an author accidentally includes the current post.

♻️ Proposed tweak
 	if (relatedSlugs && relatedSlugs.length > 0) {
 		return relatedSlugs
+			.filter((s) => s !== slug)
 			.map((s) => getBlogPost(s))
 			.filter((post): post is BlogPost => post !== undefined);
 	}
apps/marketing/src/app/blog/[slug]/components/BlogPostLayout/BlogPostLayout.tsx (1)

97-111: Extract the 200px min width to a named constant.
Avoids a magic number and makes future layout tweaks safer.

♻️ Proposed refactor
 import { GridCross } from "@/app/blog/components/GridCross";
 import { type BlogPost, formatBlogDate, type TocItem } from "@/lib/blog-utils";
 
+const RELATED_POSTS_MIN_WIDTH_PX = 200;
+
 interface BlogPostLayoutProps {
 	post: BlogPost;
 	toc: TocItem[];
 	relatedPosts: BlogPost[];
@@
 						<h2 className="text-xl font-medium text-foreground mb-6">
 							Related Posts
 						</h2>
-						<div className="grid gap-4 grid-cols-[repeat(auto-fit,minmax(200px,1fr))]">
+						<div
+							className="grid gap-4"
+							style={{
+								gridTemplateColumns: `repeat(auto-fit, minmax(${RELATED_POSTS_MIN_WIDTH_PX}px, 1fr))`,
+							}}
+						>
 							{relatedPosts.map((relatedPost) => (
 								<BlogCard key={relatedPost.slug} post={relatedPost} />
 							))}
 						</div>
As per coding guidelines: "Avoid magic numbers by extracting them to named constants at module top."

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 3, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

@saddlepaddle saddlepaddle merged commit 524935f into main Feb 3, 2026
18 of 25 checks passed
@Kitenite Kitenite deleted the add-related-posts-section-to-blog-post-pages branch February 4, 2026 20:42
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.

1 participant