Calling toLocaleString without a locale formats the value with the default
locale of whichever runtime renders it: often en-US on the server, the
visitor's language in the browser. The text then differs during hydration.
no-locale-without-explicit-locale reports locale-sensitive formatting in
render code that has no explicit locale.
| Rule | hydration-proof/no-locale-without-explicit-locale |
|---|---|
| What it reports | Require an explicit locale for locale-sensitive formatting during render |
| recommended / next | Warning |
| strict | Error |
| Server Components | Skipped with the next preset (they never hydrate) |
| Suggestions | Yes |
| Options | defaultLocale |
What it reports
In render code, when the locale argument
is missing, undefined, void 0 or []:
value.toLocaleString(),value.toLocaleDateString(),value.toLocaleTimeString()a.localeCompare(b)(the locale is the second argument)Intl.NumberFormat,Intl.DateTimeFormat,Intl.RelativeTimeFormat,Intl.PluralRules,Intl.Collator,Intl.ListFormatandIntl.DisplayNames, with or withoutnewIntl.X().resolvedOptions().locale, which reads the runtime's default locale
A variable as the locale (toLocaleString(locale)) is accepted: the rule
cannot know its value, and passing the locale from the server is the fix.
The suggestion inserts the locale from the defaultLocale option ('en-US'
by default). It is offered as a suggestion, not a fix, because the right
locale is a product decision.
Why toLocaleString without locale breaks hydration
Without a locale, formatting uses the default locale of the JavaScript
runtime. Servers usually run with en-US (or whatever the container sets);
browsers use the visitor's language:
server HTML: <p>Total: 1,234.5</p> (en-US)
client render: <p>Total: 1.234,5</p> (de-DE)React reports the text mismatch and renders the page again on the client. Locale-dependent formatting covers the fixes, including where to get the visitor's locale on the server.
Incorrect
function Price({ amount }) {
return <p>Total: {amount.toLocaleString()}</p>;
}
function Percent({ value }) {
const format = new Intl.NumberFormat(undefined, { style: "percent" });
return <span>{format.format(value)}</span>;
}
function Match({ a, b }) {
return <p>{a.localeCompare(b) === 0 ? "same" : "different"}</p>;
}Correct
function Price({ amount, locale }) {
// The locale comes from the request (cookie, URL or Accept-Language) and is passed down.
return <p>Total: {amount.toLocaleString(locale)}</p>;
}
function Percent({ value }) {
const format = new Intl.NumberFormat("en-US", { style: "percent" });
return <span>{format.format(value)}</span>;
}
function Match({ a, b }) {
return <p>{a.localeCompare(b, "en") === 0 ? "same" : "different"}</p>;
}Options
| Option | Type | Default | Description |
|---|---|---|---|
| defaultLocale | string | 'en-US' | Locale inserted by the suggestion. |
{
rules: {
'hydration-proof/no-locale-without-explicit-locale': ['warn', { defaultLocale: 'en-GB' }],
},
}defaultLocale(string, default'en-US'): the locale the suggestion inserts.
Messages
What ESLint prints for this rule, word for word:
<call>uses the default locale of whichever runtime renders it. The server's locale and the visitor's browser locale usually differ, so the text does not match during hydration. Pass an explicit locale that is the same on the server and in the browser.<call>reads the runtime's default locale, which differs between the server and the browser. Pass the locale from the server instead.- Use the '<locale>' locale.
When not to use it
When every server and every visitor uses the same locale (an internal tool with a fixed locale, for example), or when the app sets the runtime's default locale explicitly on both sides.
Related
no-timezone-without-explicit-timezone:date.toLocaleDateString()without arguments is missing both a locale and a time zone. Each rule reports its own half, at a different position, so the two reports are two separate fixes.require-deterministic-list-orderreportslocaleCompareinsidesortcomparators (the list order changes), so this rule does not.- Text content does not match server-rendered HTML, the error a locale difference produces
- Locale-dependent formatting: the cause and its fixes