Instrucciones de origen · Vista previa de solo lectura
name
seo-checklist
description
SEO first principles for PolicyEngine web applications - meta tags, crawlability, performance, and dual-mode (standalone + iframe) considerations
SEO First Principles for PolicyEngine Web Apps
Use this skill when auditing or building web applications that need to be discoverable via search engines. PolicyEngine apps are typically React SPAs deployed to GitHub Pages, often served both standalone and embedded as iframes in policyengine.org research pages.
How Search Engines Work
Google does three things:
Crawl — Googlebot fetches your URL and downloads the raw HTML response
Index — It reads that HTML, understands what the page is about, stores it
Rank — When someone searches, it picks the best matching pages from its index
Your job is to make all three steps easy. If any step fails, your page won't appear in search results.
Principle 1: Google Reads HTML, Not Your Screen
The most critical issue for React SPAs. When Googlebot visits a client-side rendered app, it sees:
<divid="root"></div>
All content generated by JavaScript may or may not be indexed. Google can execute JS but:
Pages enter a "render queue" (delays of hours to days)
JS errors = no content indexed
Google deprioritizes JS-rendered content vs static HTML
Test: Run curl -s YOUR_URL | grep -c '<h1>' — if the result is 0, Google likely can't see your content.
Solutions (ranked by effectiveness):
Approach
Description
Effort
SSR (Next.js, Remix)
Server renders full HTML on each request
High (framework change)
SSG (Static Site Generation)
Pre-build HTML at deploy time
Medium
Pre-rendering
Render SPA to static HTML for crawlers
Low-Medium
Meta tags only
At minimum, add static meta tags to index.html
Low
For PolicyEngine calculator apps, pre-rendering or SSG is the sweet spot. The form/landing page is static content; only results are dynamic.
Principle 2: One URL = One Page = One Topic
Google ranks pages, not websites. Each URL you want to rank for needs:
Google treats everything after # as the same page. All hash variations = one URL = one indexed page.
Path-Based URLs Are Crawlable
https://example.com/us/california?head=45000
Query parameters (?key=value) ARE seen by Google (though they may be treated as variants). Path segments (/us/california) are treated as distinct pages.
Principle 3: The Standalone vs Iframe Dual-Mode Problem
PolicyEngine apps often run in two modes:
Standalone — Deployed on GitHub Pages (e.g., policyengine.github.io/us-marriage-incentive/)
Embedded — Iframed inside policyengine.org research pages (e.g., policyengine.org/us/research/marriage)
SEO Implications
Concern
Standalone
Embedded (iframe)
Indexed by Google?
Yes (if crawlable)
No — Google indexes the parent page, not iframe content
Needs meta tags?
Yes — this is the version Google sees
No — parent page provides meta tags
Needs canonical URL?
Yes — should point to itself OR the parent page
N/A
Needs robots.txt?
Yes
N/A (inherits from parent domain)
Needs sitemap?
Yes
N/A (parent sitemap covers parent pages)
Canonical URL Strategy
If the primary audience should find the app via policyengine.org:
Rule: Every page needs exactly one canonical URL. Without it, Google may index both versions and split your ranking power between them (called "duplicate content dilution").
Detecting Iframe Mode in Code
Most PolicyEngine apps already detect this:
const isEmbedded = window.self !== window.top;
SEO-relevant behavior should NOT depend on this check — meta tags, titles, and structured data must be present in the static HTML regardless of runtime mode.
Principle 4: Required Meta Tags
Every PolicyEngine web app needs these in index.html:
Critical (must have)
<!-- Basic SEO --><title>US Marriage Tax Calculator — Marriage Penalty & Bonus | PolicyEngine</title><metaname="description"content="Calculate how marriage affects your taxes and government benefits. See your marriage penalty or bonus across income levels for any US state."><linkrel="canonical"href="https://policyengine.github.io/us-marriage-incentive/"><!-- Open Graph (Facebook, LinkedIn, Slack, iMessage previews) --><metaproperty="og:type"content="website"><metaproperty="og:title"content="US Marriage Tax Calculator"><metaproperty="og:description"content="Calculate how marriage affects your taxes and government benefits."><metaproperty="og:image"content="https://policyengine.github.io/us-marriage-incentive/og-image.png"><metaproperty="og:url"content="https://policyengine.github.io/us-marriage-incentive/"><metaproperty="og:site_name"content="PolicyEngine"><!-- Twitter / X --><metaname="twitter:card"content="summary_large_image"><metaname="twitter:title"content="US Marriage Tax Calculator"><metaname="twitter:description"content="Calculate how marriage affects your taxes and government benefits."><metaname="twitter:image"content="https://policyengine.github.io/us-marriage-incentive/og-image.png">
Important (should have)
<!-- Structured Data (JSON-LD) --><scripttype="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "US Marriage Tax Calculator",
"description": "Calculate how marriage affects your taxes and government benefits.",
"url": "https://policyengine.github.io/us-marriage-incentive/",
"applicationCategory": "FinanceApplication",
"operatingSystem": "Web",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"author": {
"@type": "Organization",
"name": "PolicyEngine",
"url": "https://policyengine.org"
}
}
</script><!-- Theme color for mobile browsers --><metaname="theme-color"content="#319795">
Title Tag Rules
Under 60 characters (Google truncates after that)
Primary keyword first: "US Marriage Tax Calculator" not "PolicyEngine — Calculator"
Include the brand at the end: "... | PolicyEngine"
Be specific: "Marriage Tax Calculator" not "Calculator"
Every page needs a unique title
Meta Description Rules
150-160 characters
Include a call to action ("Calculate", "Find out", "Compare")
Include primary keywords naturally
Describe what the user gets, not what the app is
OG Image Rules
Dimensions: 1200 x 630 pixels
Format: PNG or JPG
Must be an absolute URL (not a relative path)
Should visually represent the app (screenshot, branded graphic)
Place in public/ directory so it's available at build output root
Principle 5: Crawlability Files
robots.txt
Place in public/robots.txt (Vite copies public/ contents to build root):
For apps with multiple distinct pages, add each URL as a separate <url> entry.
.nojekyll (GitHub Pages only)
Always add an empty .nojekyll file to public/ when deploying to GitHub Pages. Without it, GitHub runs Jekyll processing which can mangle XML files like sitemap.xml and robots.txt, preventing Google from reading them.
GitHub Pages Sitemap Limitation
Known issue: Google Search Console cannot fetch sitemaps from .github.io domains. Even with a valid, accessible sitemap.xml, Search Console will show "Sitemap could not be read." This is a GitHub infrastructure limitation — GitHub blocks automated Googlebot fetches.
Workarounds:
Custom domain (recommended): Set up a CNAME (e.g., tool.policyengine.org) pointing to org.github.io. Sitemaps work correctly on custom domains.
URL Inspection: Manually request indexing via Search Console's URL Inspection tool — this works even when sitemap fetching doesn't.
Backlinks: Links from other indexed sites (e.g., policyengine.org embedding/linking to the tool) will cause Google to discover and index the page without needing the sitemap.
Google Analytics (GA4) — Shows traffic sources, user behavior, conversions. Free.
Integration
Add to index.html before </head>:
<!-- Google tag (gtag.js) --><scriptasyncsrc="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script><script>window.dataLayer = window.dataLayer || [];
functiongtag(){dataLayer.push(arguments);}
gtag('js', newDate());
gtag('config', 'G-XXXXXXXXXX');
</script>
Replace G-XXXXXXXXXX with the actual GA4 measurement ID.
Principle 10: Off-Page SEO (Backlinks)
The most powerful ranking signal is other websites linking to yours. For PolicyEngine:
Blog posts on policyengine.org that link to the calculator
Research papers that reference the tool
Social media shares (indirect — drives traffic, which signals value)
Being embedded on policyengine.org research pages (the parent page links/iframes the app)
This is not something the plugin can check, but it's important context: the policyengine.org embedding strategy provides backlink authority that standalone GitHub Pages deployments lack.
Quick Reference: SEO Audit Checklist
Critical (app won't rank without these)
<title> is descriptive, < 60 chars, includes keywords
<meta name="description"> is 150-160 chars with call to action