How to Add Google Translate to Your Next.js/React App (The Right Way)

Jimoh Sherifdeen / September 10, 2025

12 min read

If you've ever tried to add Google Translate to a Next.js or React application, you've probably encountered the same frustration I did. The standard guides out there are written for vanilla JavaScript websites, and when you try to implement them in a React environment, things break. The default Google Translate widget also looks terrible and doesn't match your app's design.

After struggling with the typical implementation, I built a solution that works seamlessly with Next.js (and React) while giving you complete control over the UI. Here's how to do it properly.

The Problem with Standard Google Translate Guides

Most tutorials, including popular ones like this guide from SquareKo, are designed for vanilla JavaScript websites. When you try to implement their solutions in Next.js or React, you run into several issues:

  1. Script Loading Problems: The Google Translate script doesn't load properly with React's component lifecycle

  2. DOM Manipulation Conflicts: Google Translate directly manipulates the DOM, which conflicts with React's virtual DOM

  3. Ugly Default UI: The default dropdown looks outdated and doesn't match modern app designs

  4. SSR Issues: Server-side rendering in Next.js causes additional complications

The Next.js Solution

Here's how I solved these problems with a clean, modern approach.

Step 1: Loading the Google Translate Script

First, we need to properly load the Google Translate script in our Next.js application. The key is using Next.js's Script component with the right strategy.

In your root layout file (app/layout.js for App Router or pages/_app.js for Pages Router), add:

import Script from 'next/script'; export default function RootLayout({ children }) { return ( <html lang="en"> <head> {/* Google Translate Initialization */} <Script id="google-translate-init" strategy="beforeInteractive"> {` function googleTranslateElementInit() { new google.translate.TranslateElement({ pageLanguage: 'en', includedLanguages: 'es,fr,de,it,pt,ru,ja,ko,zh', layout: google.translate.TranslateElement.InlineLayout.SIMPLE }, 'google_translate_element'); } `} </Script> {/* Google Translate Script */} <Script src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" strategy="beforeInteractive" /> </head> <body> {/* Hidden Google Translate Element */} <div id="google_translate_element" style={{ display: 'none' }}></div> {children} </body> </html> ); }

Key Points:

  • We use strategy="beforeInteractive"

    to ensure the script loads before React hydration

  • The Google Translate element is hidden since we'll create our own UI

  • We specify only the languages we want to support in

    includedLanguages

Step 2: Creating a Custom Language Selector

Now comes the fun part - building a beautiful, custom language selector that triggers the Google Translate functionality. I used Radix UI's Select component, but you can use any select component or build your own.

// components/LanguageSelector.js 'use client'; import { useState, useEffect } from 'react'; import * as Select from '@radix-ui/react-select'; import { ChevronDownIcon, GlobeIcon } from '@radix-ui/react-icons'; const languages = [ { code: 'en', name: 'English', flag: '🇺🇸' }, { code: 'es', name: 'Español', flag: '🇪🇸' }, { code: 'fr', name: 'Français', flag: '🇫🇷' }, { code: 'de', name: 'Deutsch', flag: '🇩🇪' }, { code: 'it', name: 'Italiano', flag: '🇮🇹' }, { code: 'pt', name: 'Português', flag: '🇵🇹' }, { code: 'ru', name: 'Русский', flag: '🇷🇺' }, { code: 'ja', name: '日本語', flag: '🇯🇵' }, { code: 'ko', name: '한국어', flag: '🇰🇷' }, { code: 'zh', name: '中文', flag: '🇨🇳' }, ]; export default function LanguageSelector() { const [selectedLanguage, setSelectedLanguage] = useState('en'); const [isTranslateReady, setIsTranslateReady] = useState(false) useEffect(() => { // Wait for Google Translate to be ready const checkGoogleTranslate = () => { if (window.google && window.google.translate) { setIsTranslateReady(true); } else { setTimeout(checkGoogleTranslate, 100); } }; checkGoogleTranslate(); }, []); const handleLanguageChange = (languageCode) => { if (!isTranslateReady) return; setSelectedLanguage(languageCode); // Trigger Google Translate const selectElement = document.querySelector('#google_translate_element select'); if (selectElement) { selectElement.value = languageCode; selectElement.dispatchEvent(new Event('change')); } }; if (!isTranslateReady) { return ( <div className="flex items-center gap-2 px-3 py-2 text-sm text-gray-500"> <GlobeIcon className="w-4 h-4" /> Loading... </div> ); } return ( <Select.Root value={selectedLanguage} onValueChange={handleLanguageChange}> <Select.Trigger className="inline-flex items-center justify-between gap-2 px-3 py-2 text-sm bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent min-w-[140px]"> <div className="flex items-center gap-2"> <span>{languages.find(lang => lang.code === selectedLanguage)?.flag}</span> <Select.Value /> </div> <Select.Icon> <ChevronDownIcon className="w-4 h-4" /> </Select.Icon> </Select.Trigger> <Select.Portal> <Select.Content className="bg-white border border-gray-200 rounded-md shadow-lg overflow-hidden z-50"> <Select.Viewport className="p-1"> {languages.map((language) => ( <Select.Item key={language.code} value={language.code} className="flex items-center gap-2 px-3 py-2 text-sm cursor-pointer hover:bg-gray-100 focus:bg-gray-100 focus:outline-none" > <span>{language.flag}</span> <Select.ItemText>{language.name}</Select.ItemText> </Select.Item> ))} </Select.Viewport> </Select.Content> </Select.Portal> </Select.Root> ); }

Step 3: Using the Component

Now you can use your custom language selector anywhere in your app:

// In any component import LanguageSelector from '@/components/LanguageSelector'; export default function Header() { return ( <header className="flex items-center justify-between p-4"> <h1>My App</h1> <LanguageSelector /> </header> ); }

Why This Solution Works Better

1. Proper React Integration: Uses Next.js Script component for optimal loading 2. Custom Styling: Complete control over the UI to match your app's design 3. Better UX: Clean, modern interface with flags and proper hover states 4. Performance: Only loads the languages you actually need 5. SSR Compatible: Works seamlessly with Next.js server-side rendering

Key Benefits

  • ✅ Works with both Next.js App Router and Pages Router

  • ✅ Compatible with React applications

  • ✅ Customizable UI that matches your design system

  • ✅ Better accessibility with proper ARIA attributes

  • ✅ Loading states and error handling

  • ✅ TypeScript ready (just add proper types)

Troubleshooting Tips

1. Translation not working? Make sure the Google Translate script has loaded before trying to change languages. The component includes a loading check for this.2. Styles being overridden? Google Translate adds its own CSS. You might need to add !important to some of your custom styles or hide Google's default elements.3. Content not translating? Ensure your content is in the DOM when the translation occurs. Dynamic content loaded after translation might not be translated.

Conclusion

While the standard Google Translate widget works for basic websites, modern React and Next.js applications need a more sophisticated approach. This solution gives you the power of Google Translate with complete control over the user experience.The key is properly loading the Google Translate script, hiding the default UI, and creating your own interface that programmatically controls the translation. The result is a translation feature that feels native to your application.


Have you implemented Google Translate in your React or Next.js app? Share your experience in the comments below!