SPA's Empty #root Kills SEO — How Next.js SSR Feeds Both Crawlers and AI
An SPA's empty root div is invisible to Google and ChatGPT alike. SSR delivers complete HTML that search engines index and AI models cite, so a single Next.js page earns traffic from both traditional search and AI-generated answers.
SPA architecture returns a bare <div id="root"></div> to the browser, leaving search engine crawlers with nothing to index. The root cause is client-side rendering: the JSX-plus-data equation runs in the browser, not on the server. Next.js flips this by executing that same equation in a Node environment, shipping fully populated HTML that crawlers can parse immediately. The browser then hydrates the static markup into an interactive React app, preserving the SPA user experience.
Next.js App Router enforces a file-as-route convention where every page.tsx inside the app directory maps directly to a URL. Dynamic routes like [id] let a single file serve thousands of content pages, each rendered as complete HTML on the server. Metadata exports feed <title>, <description>, and <keywords> into the <head>, covering the first layer of SEO. The second layer — actual content — arrives pre-rendered and indexable.
The same SSR output now serves a second audience: AI models. Generative Engine Optimization (GEO) requires that large language models can read and cite web content when generating answers. An SPA's empty root div is just as invisible to ChatGPT or Perplexity as it is to Googlebot. One SSR-rendered HTML page satisfies both traditional crawlers and AI citation engines, making Next.js the default choice for AI product landing pages.
GEO reframes SSR's value proposition: the same server-rendered HTML that solved a 2010s SEO problem now solves a 2020s AI-visibility problem, without any additional work.
The file-as-route convention eliminates an entire category of configuration bugs and decision fatigue — there is no router file to misconfigure, only a directory structure to follow.
SSR's core insight is mechanical, not magical: a React component is just a function that takes data and returns markup. Run it on the server, and you get HTML; run it in the browser, and you get an empty div until JavaScript executes.