# no-locale-without-explicit-locale

> Calling toLocaleString without a locale formats with the runtime's default, which differs on the server and in the browser. This rule reports it.

Source: https://hydration.jscrate.dev/docs/rules/no-locale-without-explicit-locale
Last updated: 2026-09-18

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 |
| Suggestions | Yes |
| Options | defaultLocale |

## What it reports

In [render code](https://hydration.jscrate.dev/docs/eslint#what-counts-as-render), 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.ListFormat` and
  `Intl.DisplayNames`, with or without `new`
- `Intl.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:

```text
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](https://hydration.jscrate.dev/docs/causes/locale) covers the fixes,
including where to get the visitor's locale on the server.

## Incorrect

```jsx
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

```jsx
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. |

```js title="eslint.config.mjs"
{
  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`](https://hydration.jscrate.dev/docs/rules/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-order`](https://hydration.jscrate.dev/docs/rules/require-deterministic-list-order)
  reports `localeCompare` inside `sort` comparators (the list order changes),
  so this rule does not.
- [Text content does not match server-rendered HTML](https://hydration.jscrate.dev/docs/errors/text-content-does-not-match-server-rendered-html),
  the error a locale difference produces
- [Locale-dependent formatting](https://hydration.jscrate.dev/docs/causes/locale): the cause and its fixes
