Skip to content

SEO content plays: /compare pages, FAQ schema, cookbook infra#5219

Open
NicholasKissel wants to merge 8 commits into
mainfrom
NicholasKissel/seo-content-plays
Open

SEO content plays: /compare pages, FAQ schema, cookbook infra#5219
NicholasKissel wants to merge 8 commits into
mainfrom
NicholasKissel/seo-content-plays

Conversation

@NicholasKissel

@NicholasKissel NicholasKissel commented Jun 11, 2026

Copy link
Copy Markdown
Member

Implements the SEO content plays for rivet.dev.

Play 1: Comparison pages at /compare/*

  • Typed data registry (src/data/compare/) + shared React components + one dynamic Astro route; page N+1 = one data file + one registry line
  • Rivet vs Cloudflare Durable Objects: migrated from the orphaned 1039-line RivetVsCloudflareWorkersPage.tsx (now deleted), with copy fixes and an updated SQLite row
  • Rivet vs Temporal: new page; every public claim (latency floor, actions pricing, SDK count, license, self-host architecture) verified against Temporal's own docs/pricing as of June 2026
  • Hub page at /compare/, footer link, BreadcrumbList + WebPage + FAQPage JSON-LD per page, CollectionPage/ItemList on the hub

Play 2: FAQ system + FAQPage JSON-LD

  • One TS data module per page feeds both the visible rendering and the schema, so they cannot diverge
  • Hook-free FaqSection/FaqList (native <details>, server-renders with zero JS) + FaqJsonLd.astro emitter (one FAQPage per URL)
  • FAQs on /cloud, /actors (static render, no client directive), /agent-os/pricing (existing Q&As moved to the shared system)

Play 3: Cookbook expansion (1 → 8 entries)

  • Populated templates registry (turns on cookbook index filter chips), TechArticle + BreadcrumbList JSON-LD on cookbook pages, Cookbook nav item
  • Six new wave-1 entries: AI Agent, Chat Room, Cron Jobs, Live Cursors, Database per Tenant, Collaborative Text Editor
  • AI Agent Workspaces (/cookbook/ai-agent-workspace/): agentOS pattern guide anchored on examples/agent-os; one agentOs() actor per agent covering filesystem, processes, shells, preview URLs, cron, host tools, Pi sessions, and sandbox mounting; content adversarially verified against the example and rivetkit/agent-os source; cross-linked from the AI Agent entry and the agentOS docs overview
  • One-line docs cross-links from schedule, sqlite, sqlite-drizzle, events, connections, and queues docs to their related cookbook entries

Verification

  • Full pnpm build passes (247 pages)
  • Script-asserted: every ld+json block parses, exactly one FAQPage per URL, every FAQ question visible in page HTML
  • Sitemap includes all /compare/ and /cookbook/ URLs; comparison tables are server-rendered; FAQs toggle with JS disabled

🤖 Generated with Claude Code

@railway-app

railway-app Bot commented Jun 11, 2026

Copy link
Copy Markdown

🚅 Deployed to the rivet-pr-5219 environment in rivet-frontend

Service Status Web Updated (UTC)
website 😴 Sleeping (View Logs) Web Jun 11, 2026 at 8:04 pm
kitchen-sink ❌ Build Failed (View Logs) Web Jun 11, 2026 at 5:41 pm
mcp-hub ✅ Success (View Logs) Web Jun 11, 2026 at 5:39 pm
frontend-cloud ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am
ladle ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am
frontend-inspector ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

PR Review: SEO Content Plays – /compare pages, FAQ schema, cookbook infra

Overview

This PR ships three SEO content plays for rivet.dev: a /compare/* system for competitor landing pages, a shared FAQ component+schema system, and expanded cookbook infrastructure (1 → 8 entries). The architecture is clean and the PR description is accurate. Below are observations grouped by priority.


Issues

[Minor] JSON-LD blocks outside FaqJsonLd.astro do not escape <

FaqJsonLd.astro correctly does .replace(/</g, "&lt;") before injecting JSON-LD via set:html. The same escape is missing from the breadcrumb, WebPage, TechArticle, and CollectionPage inline <script type="application/ld+json" set:html={JSON.stringify(...)} /> calls in the new .astro pages. All current values are developer-supplied literals so there is no real XSS risk today, but any future field that interpolates user-controlled text (a competitor name from a CMS, for example) would break the script element. Centralizing through FaqJsonLd.astro was the right instinct; the same helper should exist for the other schema types, or at minimum document the escape requirement in a shared helper or comment.

[Minor] Compare-page FAQ requires JavaScript

FaqSection and FaqList are deliberately hook-free with native <details>/<summary> so they work without JavaScript. The FaqSectionDark section inside ComparePage.tsx lives inside the client:load island, so the compare-page FAQ requires JS to appear — unlike the actors, cloud, and agentOS pricing pages where the same component is server-rendered. This contradicts the design goal stated in the component comments. Consider splitting the compare page into a server-rendered shell (hero, FAQ) with client islands for the motion sections, or document the accepted tradeoff.

[Nit] /compare/ pages have no active nav highlight

The /compare/ hub and /compare/[slug] pages do not pass active to MarketingLayout, so no header nav item highlights when browsing those pages. The /cookbook/ pages correctly pass active="cookbook". This is cosmetic but inconsistent.


Observations

Architecture is clean and extensible. The typed data registry (src/data/compare/) plus a single dynamic route is the right pattern: adding a third comparison page is one data file and one registry line. The FAQ system (one data module per page feeds both the visible rendering and the JSON-LD) prevents schema and content from diverging, which is the main failure mode of structured-data SEO.

FeatureStatus.tsx removes the old default arm — correct. The original RivetVsCloudflareWorkersPage.tsx silently mapped unknown statuses to yes via a default arm. The new component enumerates all four variants with no fallthrough, matching the project convention for discriminated unions.

SQLite status update from coming-soon to yes looks correct. The old comparison page marked Rivet SQLite coming-soon; the new cloudflare-durable-objects.tsx marks it yes with a docs link.

dangerouslySetInnerHTML in FaqList is safe as written. All answerHtml values are first-party static strings from src/data/faqs/, not user input. The FaqItem.answerHtml comment in types.ts documenting the allowed HTML tag subset is a good guardrail.

Temporal comparison sourcing. The temporal.tsx comment records the verification date (June 2026) and flags pricing pages as likely to churn. Including the specific doc URLs checked would make future re-verification easier since pricing pages move.

No redirect needed for the deleted component. RivetVsCloudflareWorkersPage.tsx was never associated with a public URL (no page route imported it), so no redirect is required.


Summary

Good PR. The extensible compare infrastructure and unified FAQ system are solid patterns. The main actionable item is the missing < escape in non-FAQ JSON-LD blocks; the compare-page FAQ requiring JS is worth addressing or documenting. Everything else is cosmetic or informational.

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