Build1 distinct publisher3 min readPublished
A React SPA's prerender step never touched the html tag, so crawlers that do not run JavaScript were told every Spanish post was English. The browser check that passed was reading somewhere else.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
What a Spanish post claims to be is set in two places on this site, and only one of them shows up in devtools. SEO.tsx is a React component; it runs after mount and writes attributes onto a live document [4]. scripts/prerender.mjs writes the bytes Vercel actually sends, and at commit 86569b0 a grep for hreflang inside it returned zero matches [5]. That script rewrote the head element only, which is why the lang attribute kept whatever index.html had baked in [6].
The check that passed was an honest check: open the page, read documentElement.lang, count the alternate links, get es and three tags [1]. The author's own account of the failure is the part worth keeping. "I did not verify the wrong thing here. I verified a real thing on the surface that was easier to reach." [19]
What kept it green under a second look deserves more attention than the original miss. Every page ships a link rel=alternate for the RSS feed, so grepping the served HTML for "alternate" produces a hit that has nothing to do with translations [9]. A string search confirms the string. The sitemap carried no xhtml:link alternates either [8], so nothing independent existed to contradict the browser.
The 21 August fix has prerender.mjs set lang on the html element and emit the alternates, reading src/config/translations.json, the same file src/config/translations.ts reads, because a .mjs build script cannot import a .ts module [11][12]. After it, four paired posts carry three hreflang links each [13]: twelve annotations in the served HTML [2] that until then existed only in a document no non-rendering crawler ever built. Unpaired posts get zero, which is the right number, since an hreflang pointing at a translation that does not exist is a false claim [14]. Google runs JavaScript, so the author reads the episode as delay rather than loss [10]. The engines that do not render spent the interval indexing Spanish prose declared as English.
The second bug in the same writeup is the same defect with the surface taken away. On 20 August at 22:48 the ad code read AdSense's silence three seconds after mount as a verdict of no-response, and the CSS collapsed the container to display:none while AdSense was still measuring it [15]. Nine hours and fifty-eight minutes later [1] the timeout came out, and a missing verdict became pending, which reserves space instead [16]. In production the end-of-post unit reaches data-adsbygoogle-status done and then never receives data-ad-status at all [17], so pending never ends. Measured on 23 August: that slot sat at 303px with the Advertisement label visible and zero iframes inside it, while the in-article slot reported unfilled and collapsed to 0px [18]. Two units on the page, an ad in neither [3].
Both bugs are a default picked for an unknown. no-response and pending are each a guess about what a third party's silence means, and the guess is the point where the code stops being checkable by looking at it.
Ranked by verification strength, evidence, and original report placement.
In the browser, a Spanish post on the site showed documentElement.lang === "es" and three <link rel="alternate" hreflang> tags for es, en and x-default, matching the SEO.tsx commit message.
The same site was serving <html lang="en"> on every Spanish post, with zero hreflang annotations.
The site is a React 19 SPA on Vercel with a build-time prerender step that writes the head into the static HTML so crawlers get titles and meta tags without executing anything.
SEO.tsx is a React component that runs in the browser after React mounts.
scripts/prerender.mjs writes the HTML the server actually sends; at commit 86569b0, grep -c hreflang against that file returned 0.
The prerender rewrote only the head element and never touched the html tag, so the lang attribute stayed at the value baked into index.html, which was en.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Specific first-party artifacts, no external corroboration
The claims are unusually concrete for a single-source item: a named commit with a quoted grep count, curl checks on four paired URLs including a Googlebot user-agent comparison, a sitemap check, post-fix per-post lang and hreflang counts, and per-slot DOM attribute, iframe, height and display readings with real slot IDs. What holds the score down is that every artifact is reported by the same author about his own site, none is independently reproducible from the cluster, and no automated test or log evidence backs the timelines.
One personal site, author-only deployments
The only adoption evidence is the author deploying and reverting changes on his own blog: the timeout shipped at 22:48, removed at 08:46, the prerender hreflang fix on 21 August, and a browser measurement on 23 August. Nothing in the cluster shows any other project, team or tool adopting the pattern or the remedy, so uptake is confined to a single site.
Claims stated conservatively against the artifacts
The write-up under-claims rather than over-claims: the author narrows the SEO impact himself (a delay rather than a loss for Google, exposure limited to non-rendering engines), refuses to generalise the lesson beyond 'I verified a real thing on the surface that was easier to reach', and reports the measurements that make his own two fixes look bad. The small negative reflects that the concrete evidence supports slightly more than the modest framing asserts; it is not a large gap because the scope really is one personal site.
Self-published author with monetization and audience interest
The piece is a developer writing on dev.to about his own site, so audience-building incentives are present, and the second defect concerns AdSense units carried on every post, meaning the author has a direct revenue interest in the ad slot behaviour he describes. The article also references his own earlier cross-post and a commenter on it. Countervailing: the content is self-critical and discloses failures, and no product, sponsor or paid offering is being sold in the supplied text.
Internally specific but single-source and self-reported
Confidence is moderate: the mechanism (head-only prerender leaves the html tag and hreflang untouched) is technically coherent and matched by named artifacts, and the ad state-machine failure is supported by same-load measurements of two slots. But the cluster has one publisher and one item, all evidence is first-party, no year is stated for any date, and no independent or automated signal corroborates the durations or the fixes.
build
Flue 2 bets that agents are a rendering problem, not an orchestration one1 distinct publisher
build
Four bugs that returned 200: what a 12-language site's crawler layer was quietly asserting1 distinct publisher
build
AI-written code fails the same four ways, and every gate you own reports green1 distinct publisher
build
With CRA out of React's docs, the new project default is a rendering decision1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 25, 2026