Growth #4: SSR public report summaries + make analyze pages indexable#2
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The biggest SEO lever from the funnel analysis: ~1,500 analyses/mo generated zero organic search value because analyze pages were noindex + fully client-rendered, while Google is already the #1 acquisition channel. - lib/report-meta.ts: cached (React cache) server fetch returning a discriminated result (ok/private/not_found/error); shares mapReportMeta with the API route so the mapping lives in one place. - page.tsx: real per-report <title>/description/OG from the actual report (was "Report ABC123"); index ONLY when the report loads publicly; canonical without query params folds ?fight=&source= variants; deleted reports return a real 404; private/errored stay noindex. - ReportSummary.tsx: server-rendered, crawlable summary (headline, zone, encounter list, roster) below the unchanged interactive app. Safeguards keep the ephemeral-content risk contained: private → noindex, deleted → 404, transient WCL errors → noindex + client retry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i
- kv-cache: recordRecentReport/getRecentReports back a Redis sorted set of public report codes (scored by last render). No new dependency; no-ops when shared Redis is absent (local dev). - report-meta: record each public report when it server-renders. - sitemap: emit the most recent public reports (hourly revalidate) so Google actively crawls them, alongside the static homepage + guides. - ReportSummary: add an at-a-glance stat row (encounters, kills, raiders, combat time) to the crawlable summary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i
alexandermayes
force-pushed
the
growth/ssr-indexable-reports
branch
from
July 16, 2026 22:36
3ca165c to
0d34e92
Compare
|
Deployment failed with the following error: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The highest-ceiling lever from the funnel analysis. Google is already ParseForge's #1 acquisition channel (631 submitters/30d), yet the ~1,500 monthly analyses generated zero organic search value: analyze pages were
robots: index:falseand fully client-rendered, with a placeholderReport ABC123title.Decision: index public reports, with safeguards + actively sitemap them
Warcraft Logs reports are ephemeral (users delete them / flip to private), so indexing blindly risks soft-404s dragging on domain quality. This PR indexes only reports that load publicly and neutralizes the risk:
index:true+ canonical + listed in sitemapnoindex, client shows the private-log errornotFound()) so Google drops itnoindex, client retriesChanges
lib/report-meta.ts(new) —getReportMeta(), a React-cache()d server fetch returning a discriminatedok/private/not_found/errorresult. SharesmapReportMeta()with the API route (mapping now in one place). Records each public report for the sitemap.app/analyze/[reportCode]/page.tsx— real per-report<title>/description/OG; conditional indexing;alternates.canonicalfolds?fight=&source=permutations; 404 on deleted.app/analyze/[reportCode]/ReportSummary.tsx(new) — server-rendered crawlable content: headline, zone, an at-a-glance stat row (encounters, kills, raiders, combat time), encounter list with kill/wipe, class-colored roster, WCL source link. Below the unchanged interactive app.lib/kv-cache.ts—recordRecentReport/getRecentReportsback a Redis sorted set of public report codes (scored by last render). No new dependency; no-ops without shared Redis (local dev).app/sitemap.ts— now async + hourly-revalidated; emits the most recent public reports alongside the static homepage + guides, so Google actively crawls them.app/api/report/[code]/route.ts— refactored to reusemapReportMeta.Tradeoffs / notes
wclQuery+ deduped per-request viacache()); under a WCL outagegetReportMetareturnserrorand falls back to the client instead of hard-failing.npm run buildand spot-check on the preview: a public report (summary + indexable), a private one (noindex + error UX), a bogus code (404), and/sitemap.xml.🤖 Generated with Claude Code
https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i