Hub and pair pages
What a compare section actually serves - the hub as a router or a ranked listicle, the index.md that switches between them, and everything a generated head-to-head page prints.
The fact sheets are the source; these are the pages they publish. Both read every line from those files, so nothing here is authored twice.
The hub: router, or ranked listicle
/compare is a router by default: a hero, one card per published pair, and a card per roundup so nothing is orphaned. Add an index.md beside the product files and the same URL becomes the ranked best {category} listicle - authored H1 and intro with the whole ranking inside the hero (one card per sheet, so a reader sees where every product stands before the table), the matrix, one product block per sheet in order (top pick first, yours badged), the method section, then the FAQ.
title: "The best CRM tools in 2026, ranked" * # the authored H1, and the whole <title> - never suffixed
description: * ; date: * # `date` is collection-enforced, as on every document
primaryKeyword: "crm tools" * # the query the intro's first words carry
intro: "One paragraph." * # the page's ONLY prose surface
criteria: ["...", "...", "..."] * # >= 3 lines saying how the ranking was made
faq: [{ q: "...", a: "..." }] * # 4-6, about the CATEGORY rather than one product
dateModified ; lastVerified # the hero's dates; lastVerified also draws the facts-verified pill
seo: { title: "...", description: "..." } # overrides the <title> and the meta description| Rule | Effect |
|---|---|
A field marked * is missing | The hub drops back to the router, and the build prints /{section} renders as a router, not a ranked listicle, naming the field |
index.md is not a product | It never columns the matrix, cards the ranking, or halves a pair URL |
hubSlug renames the hub alone | { slug: "compare", template: "compare", hubSlug: "saas-boilerplates" } serves the hub at /saas-boilerplates while every pair stays at /compare/{a}-vs-{b}; /compare 308s to the hub, built in |
The .md rendition follows the mode | The authored head, ranking and criteria in listicle mode; the plain product list in router mode |
The product block
One component draws every product write-up on the site: here, on both roundup kinds and on both sides of a pair page. Its parts are documented once, with the roundup. Its three link rows are derived, so a link appears exactly when its page does and never outlives it, and every outbound link to a vendor opens in a new tab as nofollow noopener.
What a pair page prints
In order, and every line of it read from the two sheets:
| Part | Read from |
|---|---|
| Hero | Both marks and the That is us badge. No dated pill - the page carries its dates in metadata, not over the H1 |
tldr | One authored sentence, above everything else. On a referee pair it is the verdict: who wins and why, with the winner's name drawn in the accent |
| The two cases | chooseA / chooseB under Choose {product} if, each with that sheet's verify links |
oursPitch | Referee pairs only: your own case against both, as one card under the two, in your own tint with your mark and price |
| The table | The shared matrix, opened on the pair's own two columns - three on a referee pair, so yours is visible |
| Verdict rows | Each rulings entry closes the rows it rules on, inside the table |
| Method line | Under the table, beside the legend. A pair page has no method section |
| The two blocks | The shared product block, full width, one per side |
| FAQ | Both sheets' questions, six at most, every answer closed |
A verdict row names its products, never their positions
"ShipFast is cheaper upfront, ours includes lifetime updates" survives a pair flipping direction; "the first is cheaper" silently means the other product the day it does. The content tests refuse a ruling naming neither side.
Referee pages: two products you do not make
A pair of two other products still publishes, and it argues rather than abstains. Its tldr
rules on the pairing - the winner is your own order, so one judgement lives in one place - and
oursPitch says what you would have the reader do instead, once, in a card that is visibly yours.
Each pitch line names a product and traces to a cell; the content tests refuse a pitch on a pair
containing your own product, which already argues its case in its own column. Which referee pairs
are worth writing at all is an editorial call, not a
mechanical one.
Compare engine
The fact sheets these pages are built from, the quality gate and the pair direction.
Comparison tables
The one table every surface here draws: columns, marks, verdict rows and the method.
Writing comparison pages
Which of these pages deserve to exist, and the shape that gets cited.
SEO kit
The metadata and markdown renditions these pages serve.
Compare engine
The /compare section, where each document is a product fact sheet rather than a page - the frontmatter it carries, the direction its pair URLs read, and the quality gate that refuses to publish a comparison nobody made.
Comparison tables
Every comparison table on the site is derived from the compare fact sheets - one source for the hub, each head-to-head and every alternatives roundup, with one row vocabulary, one set of marks and one method line.