From 3 clicks to 664: what a real SEO audit found in my React SSG site

2026年8月5日6 次浏览来源:Dev.to阅读原文

Three months ago I wrote a post about adding JSON-LD to my side project.

The result was 1,600 impressions and 3 clicks over 28 days.

Average position: 61.3 — page six of Google.

Today's Search Console, same 28-day window: Metric Then Now Clicks 3 664 (+149%) Impressions ~1,600 21,400 (+30%) Average position 61.3 23 The site is devtools.abect.com — 49 browser-based developer tools (image converters, text/code converters, SEO generators).

React 19, Vite, statically prerendered at build time, no backend for the tools themselves.

This post is two things: what actually moved the needle over three months, and what a proper technical audit found afterwards.

The second part is where it gets interesting, because I found bugs that had been live for months without me noticing.

Part 1: the boring stuff that worked No growth hacks here.

Five things, all tedious: One page per tool, not one page per category. , , — each a real URL with its own content, not .

This is the single highest-leverage decision I made.

Search intent for "jsx to html" is specific; a generic converter page cannot rank for 22 different conversions at once.

Prerendering to static HTML.

Every route is rendered to a complete HTML file at build time.

Googlebot receives a fully populated document — headings, body copy, structured data — without executing a single line of JavaScript.

React hydrates on top afterwards.

No server rendering at request time, no database in the critical path. 5,000–12,000 characters of real content per page.

Format comparison tables, framework-specific implementation guides (React, Next.js, Vue, WordPress), 10–12 FAQ items.

Not padding — the kind of thing you'd actually read if you landed there confused.

JSON-LD on every page: + + , with the FAQ schema built from the exact same array that renders the visible FAQ, so they can never drift apart.

Titles and descriptions written against a specific pain, not the format pair.

Compare: Same tool.

The second one matches what somebody actually types into Google when they're stuck.

Part 2: I aimed at the wrong niche I built this project for image conversion.

That was the whole premise — I was tired of ad-riddled converters that renamed my client's files.

Image tools were 27 of the 49 pages.

Today's top pages: Page Clicks Change JSX to HTML 491 +117% TSX to HTML 137 +813% HTML to TSX 9 new JPG to WebP 4 +100% The text converters — which I added almost as an afterthought, in a single batch, because they were easy — carry the entire site.

The image tools that motivated the whole project bring in a rounding error.

Why?

Image conversion is a saturated commodity: hundreds of sites, big domains, ad budgets. "jsx to html" is a narrow developer query that a handful of pages compete for, and most of them are low-effort.

I stumbled into a niche where a well-built page could actually win.

The lesson isn't "build text converters." It's that you find out which of your bets worked by shipping all of them and reading the data, not by reasoning about it beforehand.

I'd have bet money on the wrong one.

Part 3: five bugs a real audit found Three months in, I stopped adding features and audited the build output instead — not the source code, the actual HTML that ships.

That distinction matters, and here's what it surfaced.

1.

All my JSON-LD was rendering in React 19 automatically hoists , and into no matter where you render them.

It does not hoist inline tags.

So this, which looks perfectly reasonable: …produced a with the meta tags correctly hoisted, and the JSON-LD sitting in the middle of , hundreds of lines down the document.

Google parses JSON-LD in the body just fine, so rich results were never broken.

But some third-party validators only look in , and — more embarrassingly — my own changelog claimed I'd fixed this months earlier.

The fix: stop rendering the script at all.

Declare the schema, collect it during SSR, serialise it into in the prerender pass.

Because the component renders on both sides, hydration always match

分享