The migration that feels like a win
A team ships a rebuild — often moving toward a more modern, more interactive client-side architecture — and every internal metric looks better. Interactions feel instant. The demo is impressive. Then, weeks later, organic search traffic has quietly dropped, and nobody connects it back to the rebuild because the site “looks fine” when anyone on the team opens it in a browser.
Why it looks fine and isn't
The site looks fine to a person because a person's browser executes JavaScript. Search crawlers are increasingly capable of rendering JS too, but rendering is deferred, rate-limited, and inconsistent in ways a real user's browser is not. A page that depended entirely on client-side data fetching for its core content — the actual product description, the actual article body — is a bet that a crawler will render it correctly, on time, every time. That bet doesn't always pay off, and when it doesn't, the page gets indexed with next to nothing in it.
What we check first after a migration
Whether primary content is present in the initial server-rendered HTML, not just after client hydration
Whether metadata — title, description, canonical tags, Open Graph data — is generated per-page on the server, not injected client-side after mount
Whether URLs and slugs were preserved 1:1, since a rebuild is a common, quiet source of accidental 404s on previously-ranked pages
Whether internal linking structure survived the rebuild, since crawl depth and link equity reset more easily than teams expect
The Next.js-specific fix
This is exactly the gap Next.js's App Router is built to close — Server Components and server-side rendering mean the crawler and the user get the same fully-rendered HTML on the first request, with client-side interactivity layered on top rather than substituted in. When we migrate a client to Next.js, SEO parity against the old site is a launch requirement we verify before go-live, not a metric we check afterward and hope holds up.
