<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ihuajiu</title>
    <description>The latest articles on DEV Community by ihuajiu (@ihuajiu).</description>
    <link>https://dev.to/ihuajiu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4069111%2F4104b1f9-c895-4b4b-957e-11a986a10052.png</url>
      <title>DEV Community: ihuajiu</title>
      <link>https://dev.to/ihuajiu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ihuajiu"/>
    <language>en</language>
    <item>
      <title>I built a random Pokémon generator with Next.js and PokéAPI — here's what I learned about SEO in 2026</title>
      <dc:creator>ihuajiu</dc:creator>
      <pubDate>Sat, 08 Aug 2026 17:48:10 +0000</pubDate>
      <link>https://dev.to/ihuajiu/i-built-a-random-pokemon-generator-with-nextjs-and-pokeapi-heres-what-i-learned-about-seo-in-2732</link>
      <guid>https://dev.to/ihuajiu/i-built-a-random-pokemon-generator-with-nextjs-and-pokeapi-heres-what-i-learned-about-seo-in-2732</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1t4vt85mm6e6pozrrlm6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1t4vt85mm6e6pozrrlm6.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
A few weeks ago I shipped &lt;a href="https://pokeroll.app/?r=devto" rel="noopener noreferrer"&gt;PokeRoll&lt;/a&gt;, a free &lt;a href="https://pokeroll.app/?r=devto" rel="noopener noreferrer"&gt;random pokemon generator&lt;/a&gt;: one tap rolls 1 of 1,000+ Pokémon with stats, abilities and artwork. It also builds a random team of 6 (with gen/region/type filters), runs a shiny hunt at real 1/4096 odds, fuses two Pokémon, and flips every card into a copy-paste-ready Pokémon Showdown set.&lt;/p&gt;

&lt;p&gt;This post isn't about the game side, though. It's about what happened when I took SEO seriously on a small static-ish site in 2026 — including the parts of "SEO" that only exist because AI search engines now read your pages too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack: boring on purpose
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js App Router + ISR.&lt;/strong&gt; Every page is statically generated and revalidated in the background. After full static-ization, TTFB dropped from ~2.25s to ~100ms. For a site whose entire SEO strategy is "rank for hundreds of long-tail generator keywords," cheap and fast beats clever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PokéAPI as the only data source.&lt;/strong&gt; It gives you game-level data (base stats, types, moves). One thing it does &lt;em&gt;not&lt;/em&gt; have: recommended EV spreads — that's competitive-community knowledge, not game data. I initially went looking for it in the API before realizing the distinction. We ended up with a documented heuristic (252/252/4) instead of integrating an external dataset. Lesson: before wiring up a data source, classify what you're looking at — game data, community knowledge, or your own convention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic routes for long-tail.&lt;/strong&gt; &lt;code&gt;/type/[type]&lt;/code&gt; × 18 types, &lt;code&gt;/gen/[n]&lt;/code&gt; × 9 generations, &lt;code&gt;/by/[region]&lt;/code&gt; × 9 regions, all from templates. This is where the SEO fun starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 1: Intent matching doesn't count. Verbatim coverage does.
&lt;/h2&gt;

&lt;p&gt;My first pass at keywords was vibes-based: "the page is obviously about that, Google will figure it out." It won't.&lt;/p&gt;

&lt;p&gt;The fix was a three-step audit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Map every high-frequency query to a page.&lt;/strong&gt; I dumped the keyword list from Google autocomplete/competitor titles into a text file, then mapped each one to a landing page — enumerating &lt;em&gt;all&lt;/em&gt; dynamic route parameters (18 types × 9 gens × 9 regions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort the gaps into three buckets:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Copy gap&lt;/em&gt; — page exists, keyword doesn't → add it to metadata immediately. Cheapest win there is.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Feature gap&lt;/em&gt; — keyword needs a feature that doesn't exist (dual-type generator, etc.) → roadmap it.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Wrong intent&lt;/em&gt; — emulator/ROM/TCG queries → deliberately drop. Not the same product.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify with a script, not your eyes.&lt;/strong&gt; After editing, I grepped every keyword against the generated metadata of its page. This caught real bugs — e.g. my &lt;code&gt;random ${type} pokemon generator&lt;/code&gt; template was missing the literal word "type" for one variant, which no human review would have spotted.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lesson 2: Title real estate is front-loaded
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Put the high-frequency query at the &lt;strong&gt;front&lt;/strong&gt; of the title. My homepage title used to lead with the brand; the main keyword sat in the middle. Flipped it.&lt;/li&gt;
&lt;li&gt;Standardize the suffix as &lt;code&gt;keyword | Brand&lt;/code&gt;. I deleted a "— Fan-made Tool" suffix: 16 characters carrying zero ranking value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gotcha worth sharing:&lt;/strong&gt; I batch-replaced the title suffix across the codebase with replace-all… and the same string appeared in a &lt;em&gt;regex literal&lt;/em&gt; used to strip the suffix. The new suffix contained &lt;code&gt;|&lt;/code&gt;, which in a regex means "or". Everything silently broke. Before a bulk replace, grep the string in code logic, not just in copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 3: Meta description is a CTR game with a hard window
&lt;/h2&gt;

&lt;p&gt;140–160 characters is the window checkers enforce — too short wastes the snippet, too long gets truncated. Easy for static pages, genuinely fiddly for templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;18 type names differ by ~5 characters; 9 region game-name pairs differ by ~14. A fixed sentence won't fit all parameter values.&lt;/li&gt;
&lt;li&gt;Fixes: &lt;strong&gt;conditional filler&lt;/strong&gt; (when the Kalos game name "X &amp;amp; Y" makes the template too short, append "instantly") and &lt;strong&gt;length-tiered templates&lt;/strong&gt; (the share-card description picks one of two templates based on Pokémon name length).&lt;/li&gt;
&lt;li&gt;Validate by &lt;em&gt;counting real output for every parameter value&lt;/em&gt;, min and max — never estimate.&lt;/li&gt;
&lt;li&gt;Description doesn't rank you; it earns the click. Write a call to action ("copy it to Showdown"), not a summary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 4: GEO — optimizing for AI search engines is its own checklist
&lt;/h2&gt;

&lt;p&gt;I ran an AI-search-readiness audit (ChatGPT / Perplexity / AI Overviews citation readiness). What actually moved the needle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; at the root: site intro, key URLs, data source, contact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAQPage JSON-LD&lt;/strong&gt; generated from the same FAQ component that renders the visible page — one source of truth, so the schema can't drift from the content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real citations only.&lt;/strong&gt; For "Citations &amp;amp; Quotations" I fetched Bulbapedia's shiny-odds page and PokéAPI's homepage with curl and quoted them &lt;em&gt;verbatim&lt;/em&gt; in &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; + &lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt;. Don't paraphrase a quote, and never invent one — this took me two audit rounds to pass precisely because round one had links without actual quotations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured content means tables and lists.&lt;/strong&gt; A grid of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; cards doesn't count. I added a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; of numbers and an &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; getting-started list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author &amp;amp; freshness signals:&lt;/strong&gt; &lt;code&gt;author&lt;/code&gt; + &lt;code&gt;datePublished&lt;/code&gt; (first git commit date) + &lt;code&gt;dateModified&lt;/code&gt; (refreshed on ISR revalidation) in the WebPage schema, plus a visible byline.&lt;/li&gt;
&lt;li&gt;Heading hierarchy bugs hide in component libraries — my Pokémon name was an &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; jumping levels under an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;. When you fix the tag, remember the CSS selectors bound to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 5: Distribution is part of shipping
&lt;/h2&gt;

&lt;p&gt;Technical SEO gets you &lt;em&gt;eligible&lt;/em&gt; to rank. What gets you into the top 10 is backlinks and user behavior. My current playbook: open-source repo with keyword-rich anchor text, community posts (Reddit/Tumblr), a Product Hunt launch, and share-card URLs that canonical back to the main domain so every shared link consolidates authority instead of fragmenting it across tracking parameters.&lt;/p&gt;

&lt;p&gt;One underrated trick: my external links carry &lt;code&gt;?r=&amp;lt;channel&amp;gt;&lt;/code&gt; params for attribution, and every page has a self-referencing canonical — so link equity from all those parameterized URLs rolls up to the clean URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code is open
&lt;/h2&gt;

&lt;p&gt;The whole site is on GitHub: &lt;a href="https://github.com/ihuajiu/pokeroll.app" rel="noopener noreferrer"&gt;ihuajiu/pokeroll.app&lt;/a&gt; — Next.js App Router, ISR, zero backend, zero sign-up. If this was useful, a star helps more than you'd think.&lt;/p&gt;

&lt;p&gt;What's your experience with programmatic SEO on small sites? And has anyone actually measured whether llms.txt does anything yet? Genuinely curious.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>nextjs</category>
      <category>pokemon</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
