Skip to content

fix(site): replace FAQPage schema with Product/Offer on pricing#7763

Merged
mhartington merged 1 commit intomainfrom
fix/pricing-schema-markup
Apr 2, 2026
Merged

fix(site): replace FAQPage schema with Product/Offer on pricing#7763
mhartington merged 1 commit intomainfrom
fix/pricing-schema-markup

Conversation

@mhessdev
Copy link
Copy Markdown
Contributor

@mhessdev mhessdev commented Apr 2, 2026

Summary

  • Removed deprecated FAQPage structured data from the pricing page (Google restricted FAQ rich results to government/healthcare sites in Aug 2023)
  • Replaced with WebPage + Product/Offer JSON-LD schema describing the four pricing tiers (Free, Starter, Pro, Business)
  • Cleaned up unused createFaqStructuredData function and toPlainText helper from structured-data.ts

Test plan

  • Verify pricing page renders correctly in dev
  • Inspect page source to confirm JSON-LD contains Product/Offer schema instead of FAQPage
  • Validate JSON-LD with Google Rich Results Test
  • Confirm no other pages were using createFaqStructuredData

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated pricing page structured data from FAQ-focused metadata to pricing-plan metadata for better SEO.
    • Added pricing-specific entries for Free, Starter, Pro, and Business tiers including name, description, price, billing period, USD currency, availability, and one-year price validity.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Apr 2, 2026 4:50pm
docs Ready Ready Preview, Comment Apr 2, 2026 4:50pm
eclipse Ready Ready Preview, Comment Apr 2, 2026 4:50pm
site Ready Ready Preview, Comment Apr 2, 2026 4:50pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e604f6e0-a4ff-488b-a4dc-849c2b50d037

📥 Commits

Reviewing files that changed from the base of the PR and between 9b05d68 and 3e39b0c.

📒 Files selected for processing (2)
  • apps/site/src/app/pricing/page.tsx
  • apps/site/src/lib/structured-data.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/site/src/lib/structured-data.ts

Walkthrough

The pricing page’s structured data was changed from FAQ-oriented JSON-LD to pricing-plan JSON-LD. A new createPricingStructuredData generator was added to the library and the pricing page now emits pricing-plan JSON-LD for four tiers.

Changes

Cohort / File(s) Summary
Pricing Page
apps/site/src/app/pricing/page.tsx
Replaced import/use of FAQ structured-data with createPricingStructuredData. Introduced pricingStructuredData payload with four tiers (Free, Starter, Pro, Business) including name, description, price, and billingPeriod. Updated JsonLd id and data.
Structured Data Library
apps/site/src/lib/structured-data.ts
Added export createPricingStructuredData(tiers: PricingTier[]) that emits a JSON-LD @graph containing a WebPage node and one Product node per tier. Each product includes offers (USD), priceValidUntil computed as one year from today, availability, and conditional priceSpecification for paid tiers. Existing FAQ generator and helpers were not modified.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the primary change: replacing FAQPage schema with Product/Offer schema on the pricing page, which aligns perfectly with the changeset.

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


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.

@argos-ci
Copy link
Copy Markdown

argos-ci bot commented Apr 2, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Apr 2, 2026, 4:56 PM

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: 1

🧹 Nitpick comments (3)
apps/site/src/lib/structured-data.ts (2)

90-96: Multiple new Date() instantiations could produce inconsistent results.

Three separate new Date() calls are made here. In the extremely rare edge case where this executes across midnight (especially Dec 31 → Jan 1), you could get an inconsistent date. Create one Date object and reuse it:

♻️ Suggested refactor
-          priceValidUntil: new Date(
-            new Date().getFullYear() + 1,
-            new Date().getMonth(),
-            new Date().getDate(),
-          )
-            .toISOString()
-            .split("T")[0],
+          priceValidUntil: (() => {
+            const now = new Date();
+            return new Date(now.getFullYear() + 1, now.getMonth(), now.getDate())
+              .toISOString()
+              .split("T")[0];
+          })(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/lib/structured-data.ts` around lines 90 - 96, The
priceValidUntil assignment creates three separate Date instances which can yield
inconsistent results across a midnight boundary; fix by creating a single Date
object (e.g., const now = new Date()) and reuse it to build the future date used
by priceValidUntil (compute new Date(now.getFullYear() + 1, now.getMonth(),
now.getDate()), then call toISOString().split("T")[0]); update the code around
the priceValidUntil property to reference that single Date variable.

3-8: billingPeriod field is accepted but ignored.

The PricingTier type includes billingPeriod, but the function hardcodes "P1M" (line 99) regardless of what's passed. This creates a misleading API—callers might expect their billing period to be reflected in the schema output.

Consider either:

  1. Using tier.billingPeriod to compute the ISO 8601 duration dynamically
  2. Removing billingPeriod from the type if monthly billing is the only supported option
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/lib/structured-data.ts` around lines 3 - 8, The PricingTier
type declares billingPeriod but the code uses the hardcoded ISO duration "P1M"
instead; either use tier.billingPeriod to build the ISO 8601 duration or remove
the field from the type. Locate the code that currently emits "P1M" (search for
the literal "P1M" in structured-data.ts) and replace it with logic that: a)
accepts an ISO 8601 duration string from PricingTier.billingPeriod and uses it
directly if valid, or b) maps common values (e.g., "monthly" -> "P1M",
"yearly"/"annual" -> "P1Y") to an ISO duration, with a safe fallback to "P1M" if
missing/invalid; alternatively, if only monthly billing is supported, remove
billingPeriod from PricingTier and eliminate any API/parameter plumbing that
accepts it to avoid confusion.
apps/site/src/app/pricing/page.tsx (1)

8-33: Tier names and prices are duplicated from pricing-data.ts.

The Free, Starter, Pro, and Business tier names and prices (0, 10, 49, 129) already exist in the canonical plans object in pricing-data.ts. This duplication creates a maintenance risk—if prices or tier names change in pricing-data.ts, they must also be updated here.

The descriptions in the structured data are intentionally enriched with operation/storage details beyond what the pricing-data.ts subtitles provide, so a refactor would need to either extract those details from the points array or extend pricing-data.ts to include condensed descriptions suitable for both UI and structured data.

Consider creating a helper that derives the tier names and prices from plans to keep a single source of truth for pricing information.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/page.tsx` around lines 8 - 33,
pricingStructuredData currently duplicates tier names and prices that are
canonical in the plans object in pricing-data.ts; update the code to build the
input to createPricingStructuredData by deriving name and price from the
existing plans (e.g., map over plans) and only supply the enriched
description/billingPeriod here, or extend plans to include a condensed
description field and use that, so pricingStructuredData is generated from the
single source of truth (plans) rather than hard-coded values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/pricing/page.tsx`:
- Line 8: The pricingStructuredData is built at module load so the
priceValidUntil date is frozen at build time; move the priceValidUntil
computation out of the module-level createPricingStructuredData call and compute
it at request/render time (e.g., inside the page component or a data fetching
function) so priceValidUntil uses a fresh new Date() (or new Date(Date.now() +
365*24*60*60*1000).toISOString()) before calling createPricingStructuredData;
update references to pricingStructuredData to use the runtime value and ensure
createPricingStructuredData and the priceValidUntil variable names are what you
modify.

---

Nitpick comments:
In `@apps/site/src/app/pricing/page.tsx`:
- Around line 8-33: pricingStructuredData currently duplicates tier names and
prices that are canonical in the plans object in pricing-data.ts; update the
code to build the input to createPricingStructuredData by deriving name and
price from the existing plans (e.g., map over plans) and only supply the
enriched description/billingPeriod here, or extend plans to include a condensed
description field and use that, so pricingStructuredData is generated from the
single source of truth (plans) rather than hard-coded values.

In `@apps/site/src/lib/structured-data.ts`:
- Around line 90-96: The priceValidUntil assignment creates three separate Date
instances which can yield inconsistent results across a midnight boundary; fix
by creating a single Date object (e.g., const now = new Date()) and reuse it to
build the future date used by priceValidUntil (compute new
Date(now.getFullYear() + 1, now.getMonth(), now.getDate()), then call
toISOString().split("T")[0]); update the code around the priceValidUntil
property to reference that single Date variable.
- Around line 3-8: The PricingTier type declares billingPeriod but the code uses
the hardcoded ISO duration "P1M" instead; either use tier.billingPeriod to build
the ISO 8601 duration or remove the field from the type. Locate the code that
currently emits "P1M" (search for the literal "P1M" in structured-data.ts) and
replace it with logic that: a) accepts an ISO 8601 duration string from
PricingTier.billingPeriod and uses it directly if valid, or b) maps common
values (e.g., "monthly" -> "P1M", "yearly"/"annual" -> "P1Y") to an ISO
duration, with a safe fallback to "P1M" if missing/invalid; alternatively, if
only monthly billing is supported, remove billingPeriod from PricingTier and
eliminate any API/parameter plumbing that accepts it to avoid confusion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a86376a-5b94-4d69-87c4-2aff271bc50a

📥 Commits

Reviewing files that changed from the base of the PR and between 032322c and 9b05d68.

📒 Files selected for processing (2)
  • apps/site/src/app/pricing/page.tsx
  • apps/site/src/lib/structured-data.ts

mhartington
mhartington previously approved these changes Apr 2, 2026
Google restricted FAQ rich results to government/healthcare sites in
Aug 2023, making the FAQPage schema on the pricing page ineffective.
Replace it with WebPage + Product/Offer structured data that describes
the four pricing tiers, enabling product rich result eligibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants