Instrucciones de origen · Vista previa de solo lectura
name
seo-optimization
description
Apply 2026 on-page SEO best practices for the PixelDen Games portal — titles, meta, OG tags, schema.org structured data, canonicals, hreflang, and brand entity differentiation. Use when adding a new route, rewriting titles/metas, adding/updating JSON-LD, debugging SERP appearance, auditing an existing page, or wiring i18n alternates. Tech stack: React Router v7 + TypeScript + i18n (en/de/es). Triggers: 'SEO', 'meta tags', 'title tag', 'schema', 'JSON-LD', 'OG', 'Open Graph', 'canonical', 'hreflang', 'rich results', 'rich snippet', 'AI search', 'GEO', 'AEO', 'brand SEO'.
SEO Optimization Skill — PixelDen Games
Practical, opinionated 2026 SEO rules for this codebase. Read the Quick Checklist first; jump into a section only when you need the detail. Cite the source URLs in §13 if you have to defend a decision.
Quick checklist (apply on every new route or content rewrite)
<title> follows the per-page-type template (see §1) and stays ≤ 580 px / ~55 chars
Meta description present, 130–160 chars, leads with the benefit + action verb, includes primary keyword once
Twitter: twitter:card=summary_large_image, twitter:title, twitter:description, twitter:image, twitter:site (and twitter:image:alt if image is content)
<link rel="canonical"> set to a self-referencing absolute URL that matches the rendered locale prefix
JSON-LD added (one or more of: WebSite, Organization, BreadcrumbList, VideoGame co-typed, BlogPosting)
hreflang alternates emitted for all of en, de, es, plus x-default → en
All <img> have alt (descriptive) oralt="" + aria-hidden="true" if purely decorative
Above-the-fold image is <img> with explicit width/height and fetchPriority="high" (LCP); never CSS background-image
No noindex accidentally left in original code (see §11.5)
Brand string is exactly "PixelDen Games" (with "PixelDen" as alternateName); never lowercase "pixelden games" or "pixel den"
If you can't tick something, write a one-line comment in the route file explaining why (e.g. // no og:image: blog post still needs hero asset, T-123).
§1 Title tags
Length
Target 580 pixels width (Google truncates by pixels, not characters). That's roughly 50–55 chars for typical Latin text, longer if the title is i-l-t-heavy, shorter if W-M-uppercase-heavy.
Hard cap at 60 chars as a safe rule of thumb. Anything over 60 risks the ellipsis on desktop SERPs.
Mobile shows ~70–80 chars but Google still indexes the full string — optimize for the desktop cutoff.
Front — "PixelDen Games — Free Pixel Art Browser Games"
The homepage IS the brand. First-word match for brand searches.
Game detail page
Back — "Play Snake Online Free — PixelDen Games"
Long-tail / non-branded queries dominate. Keyword first wins click-throughs.
Blog post
Back — "{Article title} — PixelDen Games"
Same logic. The keyword is the hook.
Category / /games
Back — "Free Browser Games — PixelDen Games"
Generic head term first; brand reinforces.
Player profile
Back — "{nickname}'s Scores — PixelDen Games"
Separator
Use — (em dash, U+2014) or | (pipe) — both are visually clear and Google parses both. The repo currently uses —; stay consistent unless rewriting site-wide.
Avoid : (looks like a sub-heading prefix), › (rarely renders well), > (HTML-escaping risk).
Per-page-type templates
Homepage: "PixelDen Games — Free Pixel Art Browser Games"
Games index: "Free Browser Games — Play Pixel Art Games Online | PixelDen Games"
Game detail: "Play {GameTitle} Online Free — {Genre} Pixel Art Game | PixelDen Games"
Game detail (alt): "{GameTitle} — Free Browser {Genre} Game | PixelDen Games"
Blog post: "{Article title H1, ≤ 50 chars} — PixelDen Games"
Blog index: "PixelDen Blog — Free Browser Games News & Guides"
Leaderboard: "{Game} Leaderboard — Top Scores | PixelDen Games"
404: "Page Not Found — PixelDen Games"
The per-locale title comes from locales/{en,de,es}/games/{slug}.json (seoTitle field). Always provide all three.
Title-tag don'ts
❌ Keyword stuffing: "Snake Game Free Play Snake Online Snake Browser Game Snake" — Google will rewrite the title and may demote.
❌ Identical titles across pages — every route must be unique. Google rewrites duplicates.
❌ Title that doesn't match H1 — mismatch is one of the top reasons Google rewrites your title (Google docs).
❌ Generic "Home", "Games", "Page" — too vague to rank.
❌ ALL CAPS — Google often lowercases or rewrites.
Title tag does
✅ Primary keyword in the first 30 characters ("the hook never gets truncated").
✅ One brand mention; no | BrandName | Marketing tagline | … chains.
✅ Match <title>, og:title, twitter:title, and the visible <h1>. Drift → Google may rewrite (Google title-link).
✅ For seasonal / time-sensitive content add the year ("Best Free Browser Games 2026").
§2 Meta descriptions
Length
130–160 characters. Below 120 looks padded by SERP rewrite; above 160 truncates on desktop.
Google rewrites 62–70 % of meta descriptions (Search Engine Land 2025). Don't obsess — but a good description still influences the 30–40 % that survive and click-through-rate when used.
✅ Play Snake online free — fast-paced pixel art classic, no download, instant browser play. Climb the leaderboard at PixelDen. (155 chars)
✅ Drop colored crystals in this addictive falling-block puzzler. Free pixel art puzzle game, no install, plays in any browser. (132 chars)
❌ PixelDen offers a variety of free games for everyone. Come check us out today and enjoy our many games on our website. (generic / no keyword / no CTA)
❌ Snake. Free. Online. Play. Pixel. Browser. Game. (keyword spam — Google will rewrite)
Reduce rewrite rate
Google rewrites descriptions when:
They're missing, generic, or boilerplate-identical across pages.
They don't match what the query intent actually needs (Google pulls a passage from the body instead).
They're stuffed with keywords or read like ad copy.
To keep your description: make it a clean summary of the page's main answer and ensure the first paragraph of body content echoes the same phrasing — Google often picks the body sentence over a stale meta.
i18n
Each locale has its own description (locales/{en,de,es}/games/{slug}.json field seoDescription).
Do not machine-translate the description from English then leave it. The CTA verb ("Play" → "Spielen" → "Juega") and word order matter for the click.
§3 Open Graph (og:*) tags
Required (every page)
<meta property="og:title" content="..."> <!-- = <title> minus the brand suffix often reads better -->
<meta property="og:type" content="website"> <!-- "article" for blog posts; "video.game" is NOT a real OG type -->
<meta property="og:image" content="https://www.pixelden.io/..."> <!-- ABSOLUTE URL; never path-relative -->
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Screenshot of Snake gameplay on PixelDen">
<meta property="og:url" content="https://www.pixelden.io/games/snake"> <!-- = <link rel="canonical"> -->
<meta property="og:site_name" content="PixelDen Games">
<meta property="og:description" content="..."> <!-- 130-160 chars, can mirror meta description -->
<meta property="og:locale" content="en_US"> <!-- de_DE, es_ES per locale -->
Keep key elements (text, faces) in the center 1080 × 600 safe zone — outer 60 px gets cropped on some platforms.
File: JPG or PNG, < 1 MB. WebP works on most platforms but Facebook still has spotty WebP support — prefer PNG/JPG for og:image even though we use WebP elsewhere.
Absolute URL only. Path-relative og:image="/assets/og.png" silently fails on every scraper.
For game pages, use the landscape promo art if available, else fall back to the game thumbnail. Never use a square avatar — it gets letterboxed.
Add og:image:width + og:image:height so scrapers (LinkedIn especially) don't have to fetch the file to decide whether to crop.
website — homepage, game pages, catalog pages, profile pages (anything that's not a long-form article)
article — blog posts (/blog/$slug)
Custom video.game types exist in the spec but are rarely respected by major platforms; use website for game detail pages and let VideoGame JSON-LD carry the type signal.
Don'ts
❌ og:image pointing to an SVG (Facebook will refuse it; Twitter strips it).
❌ og:image < 600 × 315 (no large card preview).
❌ Forgetting og:url — many scrapers use it as the canonical for share dedup.
Twitter falls back to og:* for title/description/image when twitter:* is missing — but twitter:card itself is NOT inferred from OG. Without it, no card renders (2026 Twitter Card guide).
Twitter's preferred image ratio is 2 : 1 (1200 × 600 or 1200 × 675). Our 1.91 : 1 OG image still works — gets minor side-crop. If we author a Twitter-specific image, prefer 1200 × 675.
Twitter's official Card Validator was retired. To verify, post the URL into a Tweet draft and check the preview, or use a third-party OG/Twitter Card previewer for non-destructive testing.
twitter:image:alt is part of Twitter's spec — include it for accessibility (screen readers on x.com use it). Max 420 chars.
twitter:site vs twitter:creator
twitter:site — the publisher's handle (us: @PixelDenGames). Always set on every page.
twitter:creator — original author of the content. Set on blog posts if the author has a public handle; omit otherwise.
§5 Structured data (Schema.org / JSON-LD)
Universal rules
JSON-LD only. Microdata and RDFa work, but Google's docs and tooling are JSON-LD-first; sites using JSON-LD report 23 % fewer SDTT errors (JSON-LD vs Microdata comparison).
Emit via React Router's meta() export using the script:ld+json key (already the pattern in this codebase — see app/routes/($lang).games.$slug.tsx).
Multiple JSON-LD blocks are fine — one per @type. Don't try to cram everything into a single @graph unless you have a reason.
Validate with Rich Results Test before shipping any change to schema.
Schema cheat sheet — what to add where
Route
Schemas to emit
/ (homepage)
WebSite (+ SearchAction, see note), Organization, optional ItemList of featured games
/games (catalog)
BreadcrumbList, ItemList of games
/games/$slug (detail)
VideoGame co-typed with Game, BreadcrumbList, optional AggregateRating if real plays/likes data
sameAs is the single biggest signal for entity disambiguation. Each link to an authoritative external profile (Twitter, LinkedIn, GitHub, Wikidata, Crunchbase) is a "vote" telling Google "this entity = this site" (entity SEO guide).
Always use the same @id URL across pages so Google can stitch the graph together.
name = the canonical brand. alternateName covers short forms. Never invent multiple Organization blocks with different names — confuses the graph.
Note (2024+): Google retired the Sitelinks Search Box rich result on Nov 21, 2024 (source). Keeping SearchAction is still recommended for agentic / AI search engines that parse the same markup. Low cost, possible upside.
5.3 VideoGame (game detail pages)
Per Google's docs, a VideoGame-only entry will not produce a rich result — Google explicitly states: "Google doesn't show a rich result for Software Apps that only have the VideoGame type" (SoftwareApplication docs). Co-type with Game (broader spec) to maximize eligibility:
{
"@context": "https://schema.org",
"@type": ["VideoGame", "Game"],
"name": "Snake",
"description": "Classic pixel art snake game...",
"url": "https://www.pixelden.io/games/snake",
"image": "https://www.pixelden.io/assets/games/snake/thumbnail.webp",
"genre": "Arcade",
"applicationCategory": "GameApplication", // see valid values below
"operatingSystem": "Web Browser",
"gamePlatform": ["Web Browser", "HTML5"],
"playMode": "SinglePlayer", // or MultiPlayer / CoOp
"numberOfPlayers": { "@type": "QuantitativeValue", "value": 1 },
"inLanguage": "en",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
},
"publisher": { "@id": "https://www.pixelden.io/#organization" },
"aggregateRating": { // ONLY if you have real plays + ratings
"@type": "AggregateRating",
"ratingValue": "4.5",
"bestRating": "5",
"worstRating": "1",
"ratingCount": 1240 // must be ≥ 1 real interaction
}
}
applicationCategory — valid values
Use one of Google's 25 enumerated SoftwareApplication categories. For us, GameApplication is the only correct one. Do NOT use:
"Game" (not a valid applicationCategory)
"Arcade" (that's the genre)
"WebApplication" (technically valid but misses the games signal)
genre
Free-text but stick to common terms: Arcade, Puzzle, Strategy, Card, Action, Casual, Tower Defense, Roguelike, Platformer. Capitalized, singular.
aggregateRating — when to include
Only when you have real review/play data. Faking it violates Google's structured data guidelines and can trigger a manual action.
The current registry-derived heuristic in app/routes/($lang).games.$slug.tsx (4 + leaderboard.length × 0.2, capped at 5) is a placeholder. It's tolerable while we have low play counts because the number isn't "made up out of nothing", but consider replacing with likeCount-based or omitting until real reviews exist.
For the last item, item is optional per Google — but include it anyway, it never hurts.
Per-locale names: use translated breadcrumb labels (bcHome, bcGames from locales/.../meta.json) — never English-only.
Note (Jan 2025): Google removed breadcrumbs from mobile SERPs but kept them on desktop. The schema still matters because Google's crawlers use it for hierarchy inference even without visual rendering (2025 update).
5.5 BlogPosting / Article (blog posts)
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "New Free Browser Games — April 2026 Update", // MATCH the visible H1, ≤ 110 chars
"description": "Three new pixel art games hit PixelDen this month...",
"image": [ // ARRAY; provide multiple ratios if possible
"https://www.pixelden.io/assets/blog/april-update-16x9.webp",
"https://www.pixelden.io/assets/blog/april-update-4x3.webp",
"https://www.pixelden.io/assets/blog/april-update-1x1.webp"
],
"datePublished": "2026-04-12T09:00:00Z",
"dateModified": "2026-04-15T11:30:00Z",
"author": {
"@type": "Organization",
"name": "PixelDen",
"url": "https://www.pixelden.io"
},
"publisher": { "@id": "https://www.pixelden.io/#organization" },
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://www.pixelden.io/blog/april-2026-update"
},
"inLanguage": "en"
}
No required fields per Google, but headline, image, author, datePublished are the recommended set and trigger rich results (Google Article docs).
headline ≤ 110 chars and must visually match the page H1.
image minimum 696 px wide; provide an array with 16:9, 4:3, 1:1 ratios when feasible — Google picks the right one per surface.
dateModified is a freshness signal Google reads in recency-sensitive queries. Update it whenever you make substantive edits, not on cosmetic ones (don't lie).
author.url should be a real page about the author (we use PixelDen org-author since we don't have author profiles yet — fine).
Best practice: include all games in the catalog ItemList, not a paginated subset — Google appreciates the complete inventory signal and it helps internal-link discovery.
§6 Canonical URLs
Rules
Every indexable page must have one<link rel="canonical">.
Use absolute URLs. <link rel="canonical" href="/games/snake"> is silently invalid in Google's eyes (Google docs).
Self-referencing is the default and correct behavior. A page's canonical pointing to itself is what you want 95 % of the time. If a canonical points away, you're telling Google "this page doesn't deserve to rank" — only do that for true duplicates.
Canonical must agree with hreflang. If a German page's canonical points to the English version, all the German hreflang annotations are invalidated — Google picks one and it's almost always the canonical, not the hreflang (see §7 and SEO Logist write-up).
Canonical must agree with the rendered locale. A request to /de/games/snake must canonical to https://www.pixelden.io/de/games/snake, not the English root.
Match og:url — they should be the same absolute URL. Mismatches confuse share-link dedup on Facebook/LinkedIn.
When NOT to set canonical
Almost never. The two exceptions:
A duplicate page that legitimately consolidates to another (e.g. a ?utm= campaign URL → bare URL; React Router params usually handle this via redirect, not canonical).
A syndicated copy of someone else's content. (We don't syndicate, so N/A.)
Common mistakes
❌ Canonical to the wrong host (pixelden.io vs www.pixelden.io). We use www — be consistent.
❌ Canonical includes a fragment (#section) — Google strips fragments from URLs anyway; including one looks broken.
❌ Conflicting canonicals: setting one via <link> and another via Link: HTTP header — pick one method.
❌ Trailing-slash mismatch: https://www.pixelden.io/games vs /games/ — decide the site convention and stick to it. (This repo: no trailing slash.)
❌ Canonicaling a paginated page ?page=2 back to ?page=1 — Google guidance changed; let each pagination page self-canonical.
The repo already produces this via app/lib/hreflang.ts — always use that helper, never hand-roll. New routes should call hreflangLinkDescriptors(baseUrl, path) in their meta() export.
Rules
Reciprocal links: every locale alternate must include itself AND every other locale (including itself). Google's hard rule: "If page X links to page Y, page Y must link back to page X" (Google hreflang docs).
x-default points to the locale a user sees when no language preference matches. We use English (en-only, no region) as the default — same target as the bare path.
Use ISO 639-1 language codes only (en, de, es) — noten-US, de-DE, es-ES — unless we actually serve country variants. We currently serve one variant per language → language-only codes.
Common pitfalls:
uk (Ukrainian) ≠ ua. EN-UK is invalid; for British English use en-GB.
Missing self-reference — every alternate set must include the current page.
Hreflang pointing to a URL that 404s or returns noindex — Google drops the cluster.
Don't put hreflang on a noindex page. The page is excluded from indexing, the annotation is wasted.
If hreflang ever exceeds practical inline emission (many locales, many pages), move it to the XML sitemap (app/routes/sitemap[.]xml.tsx) via the xhtml:link extension. Currently inline <link> is fine for 3 locales.
§8 Brand entity differentiation (the "is this PixelDen?" problem)
@the_pixelden — Twitter pixel artist (similar handle)
generic strings like "pixel den" / "the pixel den"
This is a classic entity disambiguation problem. The fix is structural, not copywriting.
Required brand patterns
Always use PixelDen Games as the full canonical name in:
<title> brand suffix
og:site_name
Organization.name
WebSite.name
publisher.name in BlogPosting/VideoGame
First mention in body content of any page
alternateName: "PixelDen" on the Organization + WebSite schemas — covers conversational short-form without losing the canonical.
@id-anchored Organization — every other schema (BlogPosting, VideoGame) references the org with "publisher": { "@id": "https://www.pixelden.io/#organization" } instead of duplicating the org block. This is what lets Google build a single entity graph.
sameAs array on Organization — link out to every official profile (Twitter, GitHub, LinkedIn, Wikidata if/when we have a page). Each link = one disambiguation vote (entity SEO guide).
Optional but valuable: disambiguatingDescription:
"disambiguatingDescription": "PixelDen Games is a free HTML5 pixel art browser games portal at pixelden.io, not affiliated with pixeden.com (UI assets) or any individual pixel artist."
Add to the homepage Organization block. Helps LLMs (ChatGPT/Perplexity) disambiguate when summarizing.
Naming hygiene
❌ Lowercase "pixelden games" or "pixel den" in body copy — looks like a different entity to crawlers.
❌ Variant capitalizations across pages ("PixelDen", "Pixelden", "PIXELDEN") — pick one and lint for it.
✅ In casual prose, "PixelDen" alone is fine (matches alternateName).
✅ First brand mention in any new article/page should be the full "PixelDen Games" form, subsequent mentions can be "PixelDen".
Helps: brand + city/region for local entities (N/A for us, global brand).
Hurts: brand + every keyword. "PixelDen Games Free Online Browser HTML5 Pixel Art Game Portal" reads as keyword spam and dilutes the entity signal.
§9 Image SEO (the parts that move SEO, not just a11y)
Alt text
Descriptive, ≤ 100 chars. "Screenshot of Snake gameplay showing the snake about to eat an apple" beats "snake.png" and beats "Snake game".
Empty alt + aria-hidden="true" for purely decorative images (background patterns, icon dividers): <img src="..." alt="" aria-hidden="true">.
Don't keyword-stuff alt. One natural mention of the relevant keyword is plenty.
Game thumbnails: alt should be "{GameTitle} game thumbnail" or describe what's pictured, not just the game name twice.
Above-the-fold / LCP image
Always <img>, never background-image CSS — Phaser canvas excluded. This is the most common LCP killer we hit (see the lighthouse skill).
Set explicit width and height attributes (prevents CLS).
Set fetchPriority="high" on the single LCP image; don't set it on multiple images per page (priority signal gets diluted).
Set loading="eager" (the default for in-viewport images; setting loading="lazy" on the LCP image is a documented anti-pattern).
Provide srcset for responsive sources.
File format
WebP by default for content images (already the repo convention; see LazyImage component).
AVIF where Phaser/Phaser assets allow — better compression, supported by all current browsers.
OG images are the exception — JPG/PNG for max Facebook/LinkedIn compatibility (see §3).
Caching gotcha
Public assets (public/assets/**) are not Vite-fingerprinted but our nginx config sets Cache-Control: immutable, max-age=31536000. If you replace public/assets/games/snake/thumbnail.webp in place, browsers and the OG-scraper cache will keep serving the old image for a year. Always bump the filename (thumbnail-v2.webp) when content changes. (See the nginx-static-cache feedback memory.)
§10 Core Web Vitals — the SEO-relevant view
CWV is a confirmed ranking signal (since June 2021) and a tiebreaker in competitive niches. All three must pass at the 75th percentile of real-user data for the page to be flagged "good" in Search Console (Google docs).
Vital
Threshold (good)
What to optimize first
LCP
≤ 2.5 s
Hero image as <img> (not CSS bg), fetchPriority="high", preload, no late-loaded webfont blocking
INP
≤ 200 ms
Reduce main-thread JS work; avoid heavy click handlers; defer non-critical scripts; chunk long tasks
CLS
≤ 0.1
width/height on every <img>, reserved ad slot dimensions, no late-inserted DOM above current scroll position
INP replaced FID in March 2024. Old "FID < 100ms" advice is obsolete (web.dev).
43 % of sites fail INP in 2026 — the most-failed metric. For us that means: every lucide-react icon, every analytics handler, every Sentry init has to be audited for main-thread cost.
Use the lighthouse skill (task lighthouse) for local audits. Dev numbers are not prod numbers (rule of thumb: prod LCP ≈ dev LCP × 0.5).
For real-world prod numbers post-deploy, use mcp__metrifyr__psi_analyze against the public URL.
CWV + SEO: the realistic story
Content relevance is still the #1 ranking factor. CWV is a tiebreaker. But:
Pages that fail CWV have measurably higher bounce rates (24 % higher per CWV 2026 stats) — that's a user-behavior signal Google does measure indirectly.
AI search engines (ChatGPT/Perplexity browse mode) timeout slow pages — failed CWV often = zero citation opportunity.
A CWV regression won't tank a strong page, but it will sap headroom in competitive SERPs.
§11 Anti-patterns (what NOT to do in 2026)
11.1 Schema spam
❌ Don't add FAQPage to product/game pages — Google ignores it (deprecated 2026) and at one point penalized abuse.
❌ Don't add fake aggregateRating — Google can detect synthetic ratings and issues manual actions.
❌ Don't add Review to your own products written by yourself — must be user-generated.
❌ Don't co-type with random types ("VideoGame + Movie") to grab multiple rich result chances.
11.2 Meta tags that no longer matter
❌ <meta name="keywords"> — ignored by every major search engine since ~2009. Delete on sight.
❌ <meta name="revisit-after"> — never honored.
❌ <meta name="distribution"> — never honored.
❌ <meta name="generator"> — informational only, no SEO effect.
✅ Descriptive: "play Snake free in your browser" linking to /games/snake.
11.4 Content
❌ AI-generated boilerplate with no value-add ("Snake is a fun game. Play Snake today. Snake is great."). Google's helpful-content + spam updates target this directly.
❌ Same description across all locales (machine translation that hasn't been reviewed). Per-locale seoDescription should read natively.
❌ Pages with noindex in the original HTML when you DO want them indexed. Google's Dec 2024 update made it explicit: noindex in JS may cause Googlebot to skip rendering (source).
❌ Duplicate content across /, /en/, /games, /en/games — make sure the redirect-or-canonical strategy is consistent.
11.5 Robots / indexing
❌ robots.txtDisallow: + <meta name="robots" content="noindex"> on the same URL. Google can't read the noindex because it can't crawl. Page stays indexed.
❌ noindex, nofollow on a page Google should follow links from. Prefer noindex, follow so link equity flows.
❌ Putting noindex on the page during a deploy and forgetting to remove it. Add a smoke test.
11.6 Misc
❌ Setting og:image to a path-relative URL (/og.png). Scrapers see nothing.
❌ Stuffing brand into every URL slug: /games/pixelden-games-snake. Just /games/snake.
❌ Hidden text for SEO ("display:none" content with keywords) — well-known spam pattern.
§12 PixelDen-specific patterns
Where SEO code lives
Concern
File
Homepage SEO
app/routes/($lang)._index.tsx — meta() export
Game catalog
app/routes/($lang).games._index.tsx
Game detail
app/routes/($lang).games.$slug.tsx — currently the gold-standard reference for VideoGame + Breadcrumb + hreflang
Generic head ("free browser games", "html5 games")
Keyword-rich title; ItemList of all games; pillar-page content above the grid
/games/$slug
Long-tail ("play {game} online free", "{game} game browser")
"Play {Game} Online Free" title; VideoGame schema co-typed with Game; first 100 words = direct answer
/blog/$slug
Informational ("best free puzzle games 2026", "how to play …")
Article schema; H2-rich; internal links to game pages; FAQ-style sections (no FAQ schema)
/players/$nickname
Branded long-tail / user-driven
Profile-style title; minimal schema (Person optional); noindex if profile is private
Long-tail patterns that actually convert for game portals
"play X online free" — the highest-converting pattern. Title: Play Snake Online Free — Pixel Art Arcade Game. Body opens with "Play Snake online free in your browser, no download required."
"X game browser no download" — title can lead with the genre: Free Snake Game — Play Online in Browser, No Download.
"best {genre} browser games 2026" — blog pillar pages, not game detail. Update dateModified annually.
"{game} controls" / "how to play {game}" — instructional sections WITHIN the game page (anchor links #controls, #how-to-play). Don't make separate pages — splits link equity.
Aggregator vs indie portal
PixelDen is a first-party indie portal (we made the games), not an aggregator. That changes the SEO posture:
Stronger entity claim — we can credibly say "PixelDen made Snake (a pixel art reimagining)" in copy. Aggregators can't. Use this in body content to differentiate from CrazyGames/Poki.
Game publisher is us, not a 3rd party — important for VideoGame schema authenticity.
Lower play counts means aggregateRating should be deferred until real volume; faking it is more visible at small scale.
Unique-art moat — every game thumbnail is original pixel art. Use that in og:image and alt text descriptors ("original pixel art of...") for differentiation.
Internal linking patterns
Every game page links to 3+ related games (already done via RelatedGamesGrid) — keep that on every new game route.
Every blog post links to at least 2 game pages on first mention.
Catalog /games links to every game; homepage features ~6–10.
Footer has links to /games, /blog, /leaderboard, /privacy, /safety — keeps PageRank flowing to deep pages.
AI search (ChatGPT / Perplexity / Google AI Overviews)
These engines pull from the same content but with different weightings:
Perplexity — heaviest on citation density + recency. Update dateModified honestly when content meaningfully changes; add inline citations in blog posts where claims can be sourced.
ChatGPT — favors answer-first structure + domain authority. First sentence of every page should be the direct answer.
Google AI Overviews — leans on what already ranks in classic Google. Optimize for classic SEO, AI Overviews follow.
The repo already has app/routes/llms[.]txt.tsx — keep it pointing to canonical game pages and the blog index. Add new top-priority pages when they ship.
§14 Audit checklist (use when reviewing an existing route)
Run through this before declaring a page "SEO done":
<title> is unique, ≤ 60 chars, primary keyword in first 30 chars, brand at correct end (homepage front, others back)
<meta description> is unique, 130–160 chars, includes CTA verb
All four image-share tags resolve to absolute URLs (og:image, og:url, twitter:image, canonical)
og:url === canonical === current URL with correct locale prefix
og:image is 1200×630 JPG/PNG (not SVG, not WebP) and < 1 MB
twitter:card is explicitly set (not relying on OG fallback)
JSON-LD validates in Rich Results Test with zero errors (warnings OK)
BreadcrumbList has ≥ 2 items, contiguous position, per-locale name
VideoGame is co-typed with Game, has applicationCategory: "GameApplication"
Hreflang emits all three locales + x-default, all absolute URLs, self-referential included
Canonical doesn't conflict with hreflang
Brand string is PixelDen Games everywhere structured (titles, og:site_name, schema)
No FAQPage / HowTo schema (deprecated)
No <meta name="keywords"> (dead)
All <img> have alt or aria-hidden="true"
LCP image is with //
§15 Quick references (cite these in PRs / commits when defending a choice)
Check this skill first. If it's not covered, check the Google Search Central docs in §15.
Default conservative. If a rich-result type isn't on this skill's allowlist, don't add it. Schema bloat has cost (page weight, validation noise) and no upside.
Run Rich Results Test on the deployed URL before declaring a structured-data change done. Local dev has noindex headers, won't match prod parsing.
For 1-off ambiguous questions about meta/schema, prefer mirroring an existing PixelDen route that already has the pattern (game detail page is the most complete reference today).
Don't optimize for what Google "might" reward. Optimize for what Google's docs explicitly reward today. Reverse-engineering rumored signals (entity sentiment, "topical authority scores") burns time for no measurable lift.
<img>
width
height
fetchPriority="high"
No accidental noindex left in source
Page passes Lighthouse SEO category (≥ 95) — run task lighthouse