Chrome Translate Caused React Hydration Errors — Diagnosis and Mitigation
5 min read
0
Quick summary: Chrome (Google) Translate may modify the DOM (text nodes, attributes, markup) when translating a page. Those modifications can cause React/Next.js server-rendered pages to fail hydration or throw runtime errors. This post documents how I diagnosed the issue on my site, what I tried, and the recommended mitigations.
On the English site, when a user clicked Chrome's built-in "Translate to Chinese" UI, the page would immediately throw a client-side error and become unusable until refreshed. The error only occurred when using the browser's translation feature — normal browsing was fine.
Symptoms observed:
These symptoms point to a DOM mutation performed by the browser extension/feature (Google Translate) that conflicts with React's assumptions about server-rendered markup.
React Server Components / SSR + hydration assume the DOM produced on the client matches the server-rendered HTML. When Chrome Translate injects extra elements, modifies text nodes, or changes attributes (for example setting html.lang to zh), the client-side React may detect mismatches or encounter unexpected node shapes, producing hydration warnings or runtime errors.
This is a known class of issues — see related React discussions like issues mentioning Google Translate and hydration mismatches.
I created a small headless repro using Puppeteer that:
/en)document.documentElement.lang = 'zh'console, pageerror, and error events and writes them to a log fileThe script lives at scripts/repro-chrome-translate.js and writes tmp/translate-repro.log with captured messages. The initial logs showed the injection step and helped confirm client-side scripts threw errors after the mutation.
If you need to run it locally:
# Run dev server in one terminal
pnpm dev
# In another terminal (from repo root)
node scripts/repro-chrome-translate.js
# Result: tmp/translate-repro.logtranslate="no" on <html> and a meta tag <meta name="google" content="notranslate" />.ErrorBoundary to catch uncaught client errors and show a friendly fallback UI (Reload / Try again). This reduces the impact of a single exception from taking down the whole page.ProtectFromTranslate which snapshots key DOM (outerHTML of #__next children) and watches document.documentElement.lang for changes. If the browser sets lang to zh*, the component attempts to restore snapshot HTML for modified nodes.translate="no" + <meta name="google" content="notranslate" /> in the root layout for English pages. This prevents the browser from auto-translating and avoids the immediate crash.content/posts/en/).This approach favours stability and predictability: preventing the browser from mutating the DOM avoids the class of hydration issues entirely.
Root HTML (Next.js app/layout.tsx) — disabling translate:
<html lang="en" translate="no">
<head>
<meta name="google" content="notranslate" />
</head>
<body>...</body>
</html>Lightweight ErrorBoundary (concept):
// components/error-boundary.tsx
'use client'
import React from 'react'
export class ErrorBoundary extends React.Component { /* show fallback UI */ }Protect-from-translate idea (concept):
// components/protect-from-translate.tsx
// Snapshot outerHTML of critical nodes on mount
// Observe document.documentElement for lang attribute changes
// If lang becomes zh*, restore snapshots for modified nodestranslate="no" attributes: instead of globally blocking translation, you can apply translate="no" to specific components that must not be changed (headers, interactive widgets). This gives a fine-grained balance.translate="no", or guarded components) and consider providing a site-managed translation experience.If you'd like, I can:
ErrorBoundary only (no UX banner), ortranslate="no" on specific selectors (header, hero), orProtectFromTranslate to be less invasive (restore only text nodes instead of replacing outerHTML).Tell me which follow-up you prefer and I'll implement it.
— Leon — January 31, 2026