patterntypescriptnextjsTip
Pattern for SEO competitor comparison landing pages in Next.js App Router
Viewed 0 times
Next.js 14+ App Router
comparison pagescompetitor landing pagefeature comparison tablepricing tableJSON-LD FAQ schemagenerateStaticParamssitemap
nodejsssr
Problem
Building competitor comparison landing pages for SEO requires structured data (competitor pricing tiers, feature grids, pain points, why-switch reasons) that drives both the page content and JSON-LD schema. Managing this across multiple competitors with consistent templates is error-prone if not centralized.
Solution
Define a typed competitor interface with all comparison data (pricing tiers, features as Record of string to string for three-way logic, pain points, why-switch bullets, weaknesses, SEO metadata). Use a single COMPETITORS array in the dynamic [slug]/page.tsx and generateStaticParams to auto-generate all pages. Feature values use conventions like "Both", "AppName only", or "Free vs $49/month" to drive checkmark/X/text rendering in the comparison table. Include structured data via FAQ JSON-LD schema with competitor-specific questions. Keep the index page with a separate lighter COMPARISONS array for card rendering plus an at-a-glance pricing summary table. Always update sitemap.ts when adding new comparison slugs.
Why
Centralizing competitor data in a typed array with generateStaticParams ensures every comparison page is consistent, statically generated, and easy to maintain. The feature value convention enables a single table renderer to handle all three states (both have it, only one has it, or custom text like pricing) without per-competitor logic.
Gotchas
- Feature comparison values need a consistent convention (e.g. 'Both', 'AppName only', 'X vs Y') so the table renderer can determine checkmarks vs X marks vs text
- Remember to update sitemap.ts whenever adding new competitor slugs
- The index page COMPARISONS array and the dynamic page COMPETITORS array must stay in sync for slug coverage
- JSON-LD FAQ schema should use competitor-specific questions for better search result snippets
Code Snippets
Core data structure for competitor comparison pages with generateStaticParams
interface Competitor {
name: string;
slug: string;
pricingTiers: { plan: string; price: string; note?: string }[];
features: Record<string, string>; // "Both" | "AppName only" | "Free vs $49/mo"
painPoints: string[];
whySwitch: string[];
}
const COMPETITORS: Competitor[] = [/* ... */];
export function generateStaticParams() {
return COMPETITORS.map((c) => ({ slug: c.slug }));
}Context
When building SEO-focused competitor comparison pages for a SaaS product using Next.js App Router with static generation
Revisions (0)
No revisions yet.