DEV Community

Şükrü Çelik
Şükrü Çelik

Posted on

How I Built a Scalable, SEO-Optimized Multilingual Content Hub with Next.js 🚀

Building a platform that targets multiple regions and languages is always a fun architectural challenge. Recently, while scaling my pet adoption and care platform, Patify, I hit a classic roadblock: How do we serve highly dynamic, localized content (i18n) to different countries while maintaining perfect Technical SEO and lightning-fast load times?

In this post, I want to share the architecture I used combining Next.js (SSR) and a Node.js backend to build a high-performance content hub.

The Problem with Client-Side i18n 🚨

When I first started drafting the architecture, the easiest route was to use a simple client-side translation library. However, since the primary goal of our content hub is organic discovery (SEO), relying on client-side rendering (CSR) was a huge red flag.

Google's crawlers (and definitely other search engines) need to see fully rendered HTML to index content quickly and accurately. If they hit a loading spinner while the translations are being fetched, the "Crawl Budget" is wasted.

The Solution: Server-Side Rendering (SSR) & Dynamic Routing 🛠️

I decided to leverage the Next.js routing system combined with Server-Side Rendering to ensure that every language variation of the site acts as its own static-like entity for crawlers.

Here is a simplified version of how I structured the dynamic routing for the content pages:

// pages/[lang]/content/index.js (or App Router equivalent)

export async function getServerSideProps(context) {
  const { lang } = context.params;

  // Validate supported languages to prevent 404 spam
  const supportedLangs = ['en', 'de', 'tr'];
  if (!supportedLangs.includes(lang)) {
    return { notFound: true };
  }

  // Fetch localized content from the Node.js API
  const res = await fetch(`https://api.patifyapp.com/v1/content?lang=${lang}`);
  const data = await res.json();

  return {
    props: {
      articles: data.articles,
      currentLang: lang,
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Handling hreflang Meta Tags for Google 🕷️

Serving the content in different languages isn't enough; you have to explicitly tell search engines which page targets which region to avoid "Duplicate Content" penalties.

Inside the <Head> component of the layout, I dynamically generate the hreflang tags based on the current route:

<Head>
  <title>Pet Care Guides & Content Hub | Patify</title>
  <link rel="alternate" hrefLang="en" href="[https://patifyapp.com/en/content](https://patifyapp.com/en/content)" />
  <link rel="alternate" hrefLang="de" href="[https://patifyapp.com/de/content](https://patifyapp.com/de/content)" />
  <link rel="alternate" hrefLang="x-default" href="[https://patifyapp.com/en/content](https://patifyapp.com/en/content)" />
</Head>
Enter fullscreen mode Exit fullscreen mode

The Live Implementation (Testing the SSR Performance) ⚡

Theory is great, but real-world performance is what matters. By offloading the localization to the server, the Time to First Byte (TTFB) drastically improved, and Google started indexing our deeply nested, state-specific veterinary guides almost instantly.

If you want to test how this SSR localization architecture performs in a live production environment, you can check out the two main hubs I deployed:

Clicking around these hubs, you'll notice there are no client-side layout shifts when changing regions, which is a massive win for Core Web Vitals.

What's Next?

The next phase for my Node.js backend is to implement programmatic SEO to automatically generate hyper-local guides based on JSON datasets (like specific state laws for pets).

How are you handling internationalization in your Next.js projects? Do you prefer SSG over SSR for content-heavy pages? Let's discuss in the comments! 👇

Top comments (0)