GenerateSaaS
ContentAlternatives pages

Alternatives pages

The alternatives roundups - the index at /alternatives anchored to your own product and a conquest page per rival - their frontmatter, the derived table, the ranked blocks, and the guard that noindexes a thin page.

The alternatives section serves roundups, each ranking several tools against one anchor. index.md is anchored to your own product at /alternatives and is what this page describes; every other file is a conquest page at /alternatives/{rival}, which differs only in its anchor. A roundup answers "alternatives to X"; the {brand} vs {brand} query belongs to the compare engine, and each block links out to that pair page rather than trying to be one.

The demo sites show the boilerplate's own roundups. A generated project ships _template.md and a worked conquest page, _example-competitor.md, both _-prefixed so neither is ever served. Copy, rename to {rival}.md, replace every value.

Frontmatter

Fields marked * are required. A missing one is a guard error, never a build failure - see Authoring for the base fields every document shares. The table is not authored here: it derives from the compare fact sheets, and each item names the sheet it is.

title: "Best Acme alternatives, compared" *  # NOT the H1. It fills the JSON-LD name and headline, the
                                            # OG card, and the H1 only when the page lists zero tools
description: * ; date: * ; dateModified ; lastVerified: * ; author: * ; primaryKeyword ; translate
competitor: { name: "Acme", url: "/", logo: "..." } *   # YOUR product: the baseline the page lists
                                            # alternatives FOR. The headings take the name from
                                            # `config.siteName`, not from here
                                            # NO `matrix:` block - authoring one FAILS the build
methodLine: "Read from each vendor's public pages in 2026. A cross means their docs and site do
  not show it, not proof the code lacks it."   # markdown rendition only, under the matrix. A
                                            # YEAR, never a month - see the authoring rules
items: *                                    # >= 3, ONE BLOCK PER RIVAL THE TABLE COLUMNS, each
                                            # headed "{n}. Nimbus". You are a table column, never
                                            # an entry here. The order you list them in is not the
                                            # order they render in - they run in `order`
  - { name: "Nimbus", product: "nimbus" *,  # `product` IS the fact sheet: compare/nimbus.md
      url: "https://...", summary: "...", bestFor: "...",
      features: ["..."],                    # <= 5 lines, "Why people pick it". `pros` also accepted
      fallsShort: ["..."],                  # <= 5 lines, SAME neutral label on every tool. `cons` also accepted
      verdict: { chooseThem: ["..."], chooseUs: ["..."] },   # optional, markdown rendition only
      factStrip: [{ label: "Starting price", value: "$25 per seat" }],  # 4 figures, rendition only
      bottomLine: "Nimbus is the best Acme alternative for <a specific buyer>.",   # <= 28 words
      startingPrice ; logo ; screenshot }   # ALL THREE DERIVE from the linked sheet. Author one
                                            # only to override it here
betterBuy:                                  # 2-4 admissions, after the blocks and before the FAQ
  - { when: "you want the cheapest one-time entry", pick: "Nimbus",   # a tool this page compares.
      because: "..." }                      # `when` finishes the card's bold "If", so it starts
                                            # lowercase. `because` comes from the table's cells
verdict: { summary: "..." } *               # one or two sentences naming the axis, not a winner
proof ; faq ; features                      # proof beside the claim it supports; faq is 5-6

Publishing your first roundup

Write a fact sheet per tool first, your own included. The table, the prices, the marks and the screenshots all come from them, and a roundup needs three tools plus you.

Copy _template.md to index.md, fill the anchor and one items entry per tool, and point each one at its sheet with product:. Every sheet except the anchor needs one - the table columns them all.

Write what the sheets cannot know: each summary and bestFor, the fallsShort lines, the verdict, the FAQ, each bottomLine, and two to four betterBuy admissions.

Read the build summary. A missing field noindexes the page and names itself; a product naming no sheet fails the build and names the file to create.

What renders

In order: the byline, the H1, the description and the shortlist - all inside the hero - then a conquest page's whySwitch list, the derived table with its legend, one product block per rival, the better-buy cards, the FAQ, and a CTA card. The byline prints a year, never a month. The visible breadcrumb trail is off here; the BreadcrumbList schema still ships, and the rest of the graph is in the SEO kit. {Brand} below is config.siteName.

Generated stringIs
H1Best {Brand} alternatives, compared, counting the blocks it stands over. Falls back to title only when the page lists no tools
<title>Best {Brand} Alternatives in {year} (Compared). No count - it went stale whenever a rival was added. An authored seo.title wins
Block heading{n}. {Tool} from items[].name, as an h3. It does not link out; the numeral is aria-hidden
SectionIs
ShortlistA ranked card row inside the hero, no heading of its own: rank, mark, name, bestFor, a price chip. Each card jumps to its block
The tableEvery column shown, the row-label column frozen, a chip per product from three columns up. Its columns and the blocks below it are one list in one order - order on each sheet, yours first on a conquest page
Better buyTwo to four cards naming who should not buy yours, and which listed tool suits them instead
FAQFive or six questions, every answer closed but present in the HTML

The product block below is the same component the hub and pair pages use.

Block partPrints
Header rowThe mark, the numbered name, the badges and price chip, then bestFor, positioning, the summary paragraph and the tiers chips - with the sheet's 16:9 screenshot top-right at a third of the width, beside them
Full width belowEverything from the two lists down, under both columns
Two listsfeatures under Why people pick it, fallsShort under The trade-off vs {Brand}, five lines each at most
Three link rowsDerived, never authored: that sheet's verify links, then a chip per published head-to-head the tool has and the roundup about it, then Visit {Tool}. Every vendor link opens in a new tab as nofollow noopener
Verdict linesNothing. verdict.chooseThem / verdict.chooseUs reach the .md rendition only - say the same fact in features or fallsShort
CloseA bold Bottom line:

The body does not render

The frontmatter IS the page. The body reaches the .md rendition alone, as do every row's footnote and the methodLine - and never add a line saying you build one of the tools: your domain, your navbar and your H1 already say so.

The guard

The strict schema runs on every build, next dev rebuilds included.

ConditionResult
A required field is missing or emptyindexable: false: noindex, follow, out of the sitemap and llms.txt, hidden in production. The page still renders minus that block, so one typo never fails pnpm build
A reserved slug, a duplicate primaryKeyword, a misfiled external or whySwitchGuard error naming the file, same treatment
The table columns a fact sheet no item writes a block forGuard error naming every missing product. Not a build failure: a sheet is translated into every locale the moment you write it, while the blocks are authored in one, so a locale is legitimately short of blocks until the translation runs
An authored matrix:, or a product / competitor.slug naming no fact sheetThe build fails, naming the file and the fix - both would lose verified content silently. See comparison tables
An invalid or duplicated slug in config.content.sectionsConfig error: the build fails, loudly

In development a banner lists a guarded page's errors; the build prints one summary line naming the paths.

What an item derives

An item that names a product inherits its entry price, its mark and its screenshot from that fact sheet. Author any of the three here only to override it.

  • The guard still asks each item for a startingPrice or a factStrip, so an item with a derived price carries a factStrip.
  • The .md rendition quotes your starting price from pricingConfig.plans - the first priced non-free plan, in config.currency.base.
  • Locales work as they do on a fact sheet: write English, and the pipeline localizes the prose while pinning slugs, prices and marks. See Locales.

Authoring rules

RuleWhy
One roundup per anchorTwo pages about the same anchor compete for the same query
Concede in featuresA page giving a reader no reason to pick them reads as an advert
Correct a fact in its sheetNever in a sentence here: every surface reads that one file
Bump lastVerified when you re-checkIt dates the badge, and the build flags anything over 180 days old

The rest of the editorial contract - sourcing a claim, which pages deserve to exist, the shape that gets cited - is in writing comparison pages.

Roundups are ordinary blog posts

A Best {category} tools article is a blog post, not a section of its own, so it never competes with /alternatives. It writes its own table in its body - the ComparisonMatrix shortcode - rather than deriving one.

On this page