Next.js SEO: Rendering, Metadata and Hreflang Done Right
A mid-sized B2B company in Gebze relaunched its corporate site on Next.js three months ago. The developer who built it was proud of the Lighthouse score. Pages load fast, the design is clean, but organic traffic from Google has not moved, and a competitor still running on WordPress outranks them for the exact service terms they sell. The marketing manager keeps asking why a faster, newer site performs worse in search.
The symptom is empty pages in Google's cache, not a slow site
When we pulled up the cached version of the client-side rendered pages, large sections were missing. Product descriptions, pricing tables, even the main heading sometimes showed up blank in the rendered snapshot. The site felt instant to a human visitor clicking around in a browser, but a crawler reading the raw HTML response saw a shell with a loading spinner and a handful of script tags.
This is the pattern we see most often in projects that migrate from a classic CMS to Next.js without changing how the team thinks about rendering. The team assumed that because the page looks complete once JavaScript runs, it is also complete for search engines. That assumption is where the actual damage starts.
The root cause is rendering mode, not content quality
Next.js supports several rendering strategies, and the choice between them is not cosmetic. Client-side rendering sends a near-empty HTML document and builds the page in the browser using JavaScript. Server-side rendering generates the full HTML on the server for every request. Static site generation builds the HTML once at build time and serves it as a static file. Incremental Static Regeneration combines the two, serving a static page but refreshing it on a schedule or on demand.
Google's crawler can execute JavaScript, but it does so in a second rendering pass that happens after the initial crawl, and that pass is queued rather than instant. For a site with thousands of pages, or for competitive keywords where every day of indexing delay matters, relying on that second pass is a measurable disadvantage. The Gebze company's product pages were built entirely client-side, so the crawler's first pass saw almost nothing worth ranking.
Switching everything to server-side rendering is not automatically the fix either. SSR means your server generates HTML on every single request, which adds load and latency if your hosting cannot scale with traffic spikes. For a catalog of products that changes once a day, that cost buys you nothing a static page rebuilt periodically could not already deliver.

Matching rendering mode to page type
We generally map rendering choices to how often content changes and how much it matters for ranking. Marketing pages, service pages and the homepage usually fit static generation, since content changes rarely and search visibility matters most here. Product or listing pages with frequent price or stock updates work better with Incremental Static Regeneration, so pages stay mostly static but refresh without a full rebuild. User-specific dashboards or account pages can stay client-side rendered, because these pages should never be indexed anyway. Search result pages or pages built from live, user-submitted queries need server-side rendering, so each unique query still produces crawlable HTML.
The Gebze team's mistake was treating every route the same way. Once we moved the product catalog from pure client-side rendering to ISR, the cached snapshot in Google Search Console started showing full content within days instead of staying perpetually incomplete.
What the team tried first, and why it fell short
Before calling in outside help, the in-house developer tried adding more content to the page and tweaking the title tag by hand on each route. That approach treats the symptom as a writing problem, when the actual issue was structural. No amount of extra text helps if the crawler's first pass never sees it.
They also installed a generic SEO plugin pattern borrowed from WordPress habits, expecting it to populate meta tags automatically. Next.js does not work that way. Metadata in the App Router is defined through an exported metadata object or a generateMetadata function inside each route file, not through a plugin scanning page content after the fact. Without that explicit definition, Next.js falls back to defaults, which explains why many of the site's pages shared the same generic title and description in search results.
This is a common point of confusion: a framework migration does not carry over the conventions of the previous platform. Each route needs its own metadata logic, and for dynamic pages, that logic usually pulls from the same data source that renders the page content, so the title and description stay in sync with what is actually on the page.
The fix combines correct rendering, structured metadata and a real hreflang setup
Fixing the Gebze site took three coordinated changes, not one silver bullet. First, we reclassified every route by rendering mode as described above. Second, we rebuilt the metadata layer so that every page, including dynamically generated product pages, exports its own title, description and Open Graph tags based on the actual data for that page rather than a static fallback.
Third, and this turned out to be the most overlooked piece, we addressed the site's multilingual structure. The company sells into both the Turkish domestic market and export markets, and their Turkish and English pages were sitting on the same URL with a client-side language switcher. Google had no way to tell, from the HTML alone, that two different language versions existed, so it was indexing a mix of both languages under a single URL.
A correct multilingual setup on Next.js needs distinct URLs per language, either through subpaths like /en/ and /tr/ or subdomains, combined with hreflang tags that explicitly tell search engines which URL serves which language and region. The generateMetadata function is the right place to emit these alternate language links, since it has access to the current route's locale and can compute the matching URLs for every other locale version of the same page.
Routing also needs to be decided deliberately. The Next.js App Router's internationalization support handles locale detection and path prefixing, but it does not decide your content strategy for you. A team selling only within Türkiye may not need a multilingual structure at all, and setting one up without a real second audience just doubles the content maintenance work for no ranking benefit. The Gebze company did need it, because a meaningful share of their buyers search in English.

What this fix costs, and where its limits are
Restructuring rendering and metadata on an existing Next.js site is not a weekend task once the site has more than a handful of dynamic routes. In our experience, a project of this size, correcting rendering mode per route, rebuilding metadata generation and adding a proper multilingual structure, typically falls closer to several weeks than several days, depending on how many route types exist and how much the data layer needs to change to support per-page metadata.
There is a real trade-off here. Static generation gives you strong crawl visibility and fast page loads, but any page built with SSG needs a rebuild or a revalidation trigger to reflect new content, which means your deployment pipeline has to account for that. Teams that update content constantly and are not ready to manage revalidation windows sometimes choose SSR for those specific pages and accept the added server cost instead.
It is also worth being clear about what this fix does not do. Correct rendering and metadata make your pages crawlable and properly labeled, but they do not guarantee ranking position, because ranking still depends on content relevance, backlink profile and competition for that keyword. What this work removes is the structural ceiling that was capping the site's visibility regardless of how good the content was.
For a business weighing whether to build a new corporate site or keep patching an old one, this is also a useful checkpoint: ask whoever handles your web design and development work how they plan to handle rendering mode and metadata before the build starts, not after launch. If you want to see how a comparable multilingual, SEO-structured site came together, our portfolio has examples worth reviewing.
Teams that want an outside review of an existing Next.js site, rather than a full rebuild, can also treat this as a focused audit. Rendering mode per route, metadata completeness, and hreflang correctness are three things that can be checked and fixed without touching the rest of the codebase.
If your Next.js site is getting indexed but not ranking where it should, or if you are planning a multilingual expansion and want the technical foundation right from the start, you can get a quote and walk through what your current setup looks like before deciding on next steps.




