The Architectural Shift: From Client-Side to Server-First
When we build internationalized (i18n) applications in Vite, we typically rely on client-side libraries like react-i18next or react-intl. These tools work by loading JSON translation files into the browser's memory and toggling strings dynamically.
However, migrating to Next.js changes the fundamental mental model. In Next.js, i18n isn't just about UI strings; it's about routing, SEO, and leveraging Server Components. If you approach a Next.js migration using only your old Vite patterns, you'll miss out on the performance benefits of Static Site Generation (SSG) and Server-Side Rendering (SSR).
In this guide, we will look at how to port your i18n logic from a Vite environment to Next.js (App Router) without losing your mind or breaking your translation workflows.
1. Mapping the Translation Strategy
In a standard Vite + react-i18next setup, you likely have an i18n.ts file that looks like this:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
i18n.use(initReactI18next).init({
resources: {
en: { translation: { welcome: "Hello" } },
fr: { translation: { welcome: "Bonjour" } },
},
lng: 'en',
fallbackLng: 'en',
});
In Next.js, specifically with the App Router, moving to a dynamic segment like /[locale]/page.tsx is the gold standard. This allows the server to know the language before the page is even sent to the user, eliminating that "flicker" of untranslated text often seen in Vite SPAs.
2. Setting up the Directory Structure
To keep things clean, move your existing translation files from src/locales/*.json to the root messages/ or public/locales/ directory.
Next, wrap your application in a locale-based folder structure:
app/
[locale]/
layout.tsx
page.tsx
components/
middleware.ts
The middleware.ts is crucial here. It detects the user's preferred language (via headers or cookies) and redirects them to the appropriate /[locale] path.
3. Bridging the Hook Gap
One of the biggest pain points in migration is the useTranslation() hook. In Vite, this hook is purely client-side. In Next.js, you have two choices:
- Client Components: Use a library like
next-intlornext-i18nextwhich provides a similar hook. - Server Components: Fetch translations directly via a function call, avoiding the need for hooks entirely.
For example, using next-intl in a Server Component:
import {getTranslations} from 'next-intl/server';
export default async function ProfilePage() {
const t = await getTranslations('Profile');
return <h1>{t('title')}</h1>;
}
4. Automating the Heavy Lifting
Manually rewriting every component from a client-side Vite structure to a Next.js App Router structure can be incredibly time-consuming, especially when dealing with complex providers and localized routing logic. If you're looking to speed up this process, you can use ViteToNext.AI to automatically convert your Vite components and folder structure into a Next.js-ready format. This helps handle the boilerplate of switching from React Router to Next.js routing while maintaining your i18n context.
5. Handling Global State and Context
In a Vite app, your i18n instance is often a singleton. In Next.js, you must be careful not to share state across requests on the server. Always instantiate your i18n configuration per request.
If you use react-i18next, ensure you use the I18nextProvider only in Client Components and pass the initial state (translations) from the Server Component to avoid a re-hydration mismatch error.
6. Metadata and SEO
One significant advantage of migrating is the generateMetadata function. In Vite, you likely used react-helmet. In Next.js, you can generate localized SEO tags effortlessly:
export async function generateMetadata({ params: { locale } }) {
const t = await getTranslations({ locale, namespace: 'Metadata' });
return {
title: t('title'),
description: t('description')
};
}
Conclusion
Migrating an i18n-heavy Vite app to Next.js is more than a find-and-replace task. It requires shifting your mindset toward server-side data fetching and locale-aware routing. By utilizing middleware for language detection and adopting Server Components for static segments, you can achieve better SEO and faster Page Speed Insights scores compared to a standard SPA.
Take the migration one module at a time, start with the routing middleware, and then gradually move your components to use server-side translation fetchers.
Further reading: How to automate your Vite to Next.js migration
Top comments (0)