<?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: Pikiplay Games</title>
    <description>The latest articles on DEV Community by Pikiplay Games (@pikiplay).</description>
    <link>https://dev.to/pikiplay</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%2F4217253%2Ffba04017-0cf7-4f13-b126-b4431ac14c54.png</url>
      <title>DEV Community: Pikiplay Games</title>
      <link>https://dev.to/pikiplay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pikiplay"/>
    <language>en</language>
    <item>
      <title>I built a browser game portal with Next.js: what I learned about SEO</title>
      <dc:creator>Pikiplay Games</dc:creator>
      <pubDate>Fri, 30 Oct 2026 12:33:04 +0000</pubDate>
      <link>https://dev.to/pikiplay/i-built-a-browser-game-portal-with-nextjs-what-i-learned-about-seo-1b00</link>
      <guid>https://dev.to/pikiplay/i-built-a-browser-game-portal-with-nextjs-what-i-learned-about-seo-1b00</guid>
      <description>&lt;p&gt;I'm a Unity developer, and I recently built PikiPlay, a free browser game portal, with Next.js 15, Supabase and Tailwind. Games come from partner feeds plus a few of my own. The catalog keeps growing, so SEO couldn't be a one-time checklist. Here is what I ended up doing, and what I'd tell myself at the start.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One game, one URL
Games used to be reachable by numeric id, by a partner code and by a few older slug spellings. To Google, that is duplicate content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now /game/[id] resolves the game, computes its canonical slug, and sends everything else there with a permanent redirect:&lt;/p&gt;

&lt;p&gt;const canonical = gameSlug(game);&lt;br&gt;
if (decodeURIComponent(id).trim() !== canonical) {&lt;br&gt;
  permanentRedirect(&lt;code&gt;/game/${canonical}&lt;/code&gt;);&lt;br&gt;
}&lt;br&gt;
The page also sets a canonical URL in generateMetadata, so the redirect and the tag agree.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sitemaps that survive a growing catalog
A sitemap file is limited to 50,000 URLs, and my catalog only goes up. So /sitemap.xml is an index, and games are split into files of 10,000 URLs each, served from a route handler that reads from Supabase in pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two details that mattered:&lt;/p&gt;

&lt;p&gt;I only emit , a real  and the image. Google ignores  and , and it stops trusting  if it is always "now".&lt;br&gt;
Responses are cached (s-maxage with stale-while-revalidate) so crawlers don't hammer the database.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Structured data with stable &lt;a class="mentioned-user" href="https://dev.to/ids"&gt;@ids&lt;/a&gt;
Instead of a separate WebSite object on every page, all pages point at one Organization and one WebSite by &lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;. Game pages add VideoGame, and every page adds a BreadcrumbList. Then Google can tie pages, brand and logo together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One small security point: JSON.stringify leaves &amp;lt; alone, so text from a feed containing  could break out of the JSON-LD tag. I escape it:&lt;/p&gt;

&lt;p&gt;JSON.stringify(data).replace(/&amp;lt;/g, '\u003c');&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Don't index thin pages&lt;br&gt;
Tag pages are a great way to target topic keywords ("card games", "racing games"), but a tag with two games is thin content. Tag pages with too few games render with robots: { index: false, follow: true }, so they stay crawlable for link discovery without diluting the site.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ISR instead of fully dynamic pages&lt;br&gt;
Game pages revalidate every 24 hours and tag pages every 12 hours. Crawlers get fast static HTML, and the catalog still stays fresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keyword research as code, not a spreadsheet&lt;br&gt;
I keep a typed keyword list in the repo (keyword, search volume, SEO difficulty, target page, status). The rules I follow:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Prefer low difficulty with real volume, not the biggest numbers.&lt;br&gt;
If a keyword is a game name, it maps to that game's page, never to a category.&lt;br&gt;
If I don't host the game, the keyword stays "opportunity" until I pick a stand-in.&lt;br&gt;
Every target URL must return 200 before it goes in the list.&lt;br&gt;
I use Ubersuggest to find competitor keywords and to track rankings. The list lives in version control, so a keyword change is a reviewable diff.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Telling search engines about new pages
I also have an IndexNow endpoint that submits URLs to Bing and others, so new pages need not wait for the next crawl.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What I got wrong&lt;br&gt;
I thought on-page SEO was enough. Bing Webmaster Tools now flags that the site has few links from high-quality domains. Technical SEO gets you crawled; backlinks are what's missing for ranking.&lt;br&gt;
[Add 1-2 real mistakes of your own here: something that broke, a page that got deindexed, a wrong canonical, etc.]&lt;br&gt;
Results so far&lt;br&gt;
[Fill in with real numbers: indexed pages in Search Console, impressions/clicks, how long it took. If numbers are small, say so. Honest small numbers get more trust than vague claims.]&lt;/p&gt;

&lt;p&gt;Takeaways&lt;br&gt;
One canonical URL per page, enforced by redirects.&lt;br&gt;
Split sitemaps from day one if the catalog grows.&lt;br&gt;
Don't index thin pages.&lt;br&gt;
Keep keyword mapping in code and check that targets return 200.&lt;br&gt;
Technical SEO is the floor, not the ceiling. Plan for links early.&lt;br&gt;
If you've built something similar, I'd love to hear what worked for you.&lt;/p&gt;

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