why-hydration 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -0
- package/README.md +155 -35
- package/dist/chunk-CER5NQRR.js +1333 -0
- package/dist/chunk-CER5NQRR.js.map +1 -0
- package/dist/{chunk-AS5DZZHI.js → chunk-KP4S6CRL.js} +560 -230
- package/dist/chunk-KP4S6CRL.js.map +1 -0
- package/dist/{chunk-AT6A77X3.cjs → chunk-QFHUKJ5K.cjs} +568 -229
- package/dist/chunk-QFHUKJ5K.cjs.map +1 -0
- package/dist/chunk-RWMRFTQ4.cjs +1356 -0
- package/dist/chunk-RWMRFTQ4.cjs.map +1 -0
- package/dist/index.cjs +25 -17
- package/dist/index.d.cts +37 -6
- package/dist/index.d.ts +37 -6
- package/dist/index.js +1 -1
- package/dist/next/index.cjs +4 -4
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +2 -2
- package/dist/react.cjs +4 -4
- package/dist/react.d.cts +24 -3
- package/dist/react.d.ts +24 -3
- package/dist/react.js +2 -2
- package/dist/{types-6wTih14e.d.cts → types-C14vcpBO.d.cts} +9 -0
- package/dist/{types-6wTih14e.d.ts → types-C14vcpBO.d.ts} +9 -0
- package/package.json +22 -4
- package/dist/chunk-3I6F4K4L.js +0 -642
- package/dist/chunk-3I6F4K4L.js.map +0 -1
- package/dist/chunk-AS5DZZHI.js.map +0 -1
- package/dist/chunk-AT6A77X3.cjs.map +0 -1
- package/dist/chunk-OFVFNPE6.cjs +0 -665
- package/dist/chunk-OFVFNPE6.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Hebrew and Persian overlays, live prop updates, one report per invalid nesting.
|
|
8
|
+
|
|
9
|
+
**Hebrew and Persian**
|
|
10
|
+
|
|
11
|
+
- Pages whose `<html lang>` is Hebrew (`he`, `iw`) or Persian (`fa`, `prs`,
|
|
12
|
+
`pes`) now get the overlay in that language, right-to-left, alongside the
|
|
13
|
+
Arabic one. `overlay={{ locale }}` accepts `'he'` and `'fa'` too. Other
|
|
14
|
+
right-to-left languages, such as Urdu, keep the English left-to-right panel.
|
|
15
|
+
|
|
16
|
+
**`<HydrationInspector>` props apply while the app runs**
|
|
17
|
+
|
|
18
|
+
- Props were read once, at mount, and later changes were ignored until a
|
|
19
|
+
reload. Now a new `onReport` receives the next report, a new `overlay`
|
|
20
|
+
(on/off, `position`, `locale`) rebuilds the panel with the reports so far,
|
|
21
|
+
and new `ignore`, `classify` and `maxReports` apply from then on. Inline
|
|
22
|
+
values re-created on every render do not rebuild anything.
|
|
23
|
+
|
|
24
|
+
**Invalid HTML nesting is one report**
|
|
25
|
+
|
|
26
|
+
- A `<div>` inside a `<p>` used to surface as three wrongly labelled reports
|
|
27
|
+
("browser-only API", "viewport branching" twice) next to React's own
|
|
28
|
+
warning, and a real text change inside it was never reported as one. The
|
|
29
|
+
browser repairs the server's HTML while parsing it, but React builds the
|
|
30
|
+
client DOM node by node, so the client side is now repaired the same way
|
|
31
|
+
before comparing. The nesting is one report at the misplaced element, and a
|
|
32
|
+
text difference inside it is its own report. Covers block elements in `<p>`,
|
|
33
|
+
nested `<a>`/`<button>`/`<form>`, and table content outside its section or
|
|
34
|
+
row, which the parser moves out of the whole table.
|
|
35
|
+
|
|
36
|
+
**Other fixes**
|
|
37
|
+
|
|
38
|
+
- A `roots` selector that matches nothing on the page — usually a typo — is
|
|
39
|
+
warned about once the settling window closes, instead of being skipped
|
|
40
|
+
silently.
|
|
41
|
+
- Reports no longer show the wrong component. A report parsed from one of
|
|
42
|
+
React's messages borrowed the component of whichever error came last, and
|
|
43
|
+
a text node the client added pointed at nothing, so its lookup fell back
|
|
44
|
+
the same way (a `localStorage` read labelled `<InvalidNesting>`). Each
|
|
45
|
+
report now carries only its own context, and an added text node points at
|
|
46
|
+
its parent element — which also lets `ignore` match it.
|
|
47
|
+
- Adjacent text nodes (`{label}: ` in JSX) are kept apart when the client
|
|
48
|
+
side of an invalid nesting is repaired, matching React's server HTML, so
|
|
49
|
+
they no longer show up as a bogus text change.
|
|
50
|
+
- The Release workflow no longer fails on every merge when no `NPM_TOKEN` is
|
|
51
|
+
set: it skips publishing with a notice and still opens version PRs.
|
|
52
|
+
- A GitHub Pages site (`docs/`) with search metadata, structured data and a
|
|
53
|
+
sitemap. A test keeps its error list identical to the README's.
|
|
54
|
+
|
|
55
|
+
### Patch Changes
|
|
56
|
+
|
|
57
|
+
- Make the package easier to find.
|
|
58
|
+
|
|
59
|
+
- The README has a "Searching for this error?" section quoting React's
|
|
60
|
+
hydration error messages exactly as React 18 and React 19 print them, so a
|
|
61
|
+
search for the error leads here. A test reads the list back out of the
|
|
62
|
+
README and checks the package recognizes every message on it.
|
|
63
|
+
- The npm description and keywords now cover the terms people search for:
|
|
64
|
+
Next.js, React 18 and 19, "hydration failed", "text content does not match",
|
|
65
|
+
`suppressHydrationWarning`, RTL and Arabic.
|
|
66
|
+
|
|
67
|
+
No code changes to this part.
|
|
68
|
+
|
|
69
|
+
## 0.2.0
|
|
70
|
+
|
|
71
|
+
### Minor Changes
|
|
72
|
+
|
|
73
|
+
- The overlay now speaks Arabic, and renders right-to-left data correctly in any language.
|
|
74
|
+
|
|
75
|
+
**Arabic overlay**
|
|
76
|
+
|
|
77
|
+
- On a page whose `<html lang>` is Arabic, the whole panel is in Arabic and
|
|
78
|
+
laid out right-to-left — title, buttons, category names, explanations, fixes,
|
|
79
|
+
and the scroll hint, with correct Arabic plurals. Every other page, including
|
|
80
|
+
right-to-left pages in languages the overlay does not translate, gets the
|
|
81
|
+
English panel as before.
|
|
82
|
+
- New `overlay={{ locale: 'auto' | 'en' | 'ar' }}` option. `'auto'` is the
|
|
83
|
+
default and follows `<html lang>`.
|
|
84
|
+
- The console output and the `explanation`/`suggestion` strings in `onReport`
|
|
85
|
+
stay English and are byte-identical to 0.1.5. Every built-in cause now also
|
|
86
|
+
carries `messageId` and `params`, which is how the overlay finds the
|
|
87
|
+
translation.
|
|
88
|
+
|
|
89
|
+
**Right-to-left rendering fixes**
|
|
90
|
+
|
|
91
|
+
- Arabic values no longer have their sentence punctuation moved to the wrong
|
|
92
|
+
end. Each value cell takes its direction from its own content, in either
|
|
93
|
+
panel language.
|
|
94
|
+
- Arabic values are drawn in a proportional face instead of a monospace
|
|
95
|
+
fallback, which rendered them with their letters disconnected.
|
|
96
|
+
- Selectors, component names, file paths and attribute names are isolated
|
|
97
|
+
left-to-right, and quoted values inside explanations are isolated one by one,
|
|
98
|
+
so brackets cannot mirror and tokens cannot swap places.
|
|
99
|
+
- The panel declares its own `lang`. Previously an English panel on an Arabic
|
|
100
|
+
page inherited `lang="ar"`, so screen readers voiced it with an Arabic voice.
|
|
101
|
+
|
|
102
|
+
**Other fixes**
|
|
103
|
+
|
|
104
|
+
- Invisible bidi marks (LRM, RLM, isolates…) are drawn as labelled badges in
|
|
105
|
+
the value cells, so the bidi-mark case no longer shows two identical-looking
|
|
106
|
+
values. Value cells also preserve whitespace, so whitespace-only mismatches
|
|
107
|
+
are visible.
|
|
108
|
+
- Component attribution from a React component stack no longer names a DOM tag
|
|
109
|
+
such as `<p>`; it names the nearest component you wrote.
|
|
110
|
+
- React's bare "hydration failed" message no longer adds an empty card next to
|
|
111
|
+
a real mismatch. When it is the only signal it is still reported, with text
|
|
112
|
+
that says React did not name the node, instead of "inspect the values above".
|
|
113
|
+
- React 19 hydration errors no longer produce junk reports. React 19 prints a
|
|
114
|
+
bulleted list of possible causes above its diff tree, and each bullet was
|
|
115
|
+
read as a removed ("server") diff line — five bogus "Unknown" reports per
|
|
116
|
+
mismatch, with the real client value paired against the first bullet. Only
|
|
117
|
+
the tree after React's hydration-mismatch link is read as a diff now.
|
|
118
|
+
|
|
3
119
|
## 0.1.5
|
|
4
120
|
|
|
5
121
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](#install)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
|
-
📦 **npm:** https://www.npmjs.com/package/why-hydration · 🐙 **GitHub:** https://github.com/razan-aboushi/why-hydration · 💼 **Author:** [Razan Aboushi](https://www.linkedin.com/in/razan-aboushi/)
|
|
10
|
+
🌐 **Site:** https://razan-aboushi.github.io/why-hydration/ · 📦 **npm:** https://www.npmjs.com/package/why-hydration · 🐙 **GitHub:** https://github.com/razan-aboushi/why-hydration · 💼 **Author:** [Razan Aboushi](https://www.linkedin.com/in/razan-aboushi/)
|
|
11
11
|
|
|
12
12
|
**Tells you which component broke hydration, what differed, and how to fix it — in dev, with zero production cost.**
|
|
13
13
|
|
|
@@ -60,9 +60,10 @@ together.
|
|
|
60
60
|
incidental page noise.
|
|
61
61
|
- 🌐 **Arabic-first locale detection** — digit-script mismatches (٠١٢ vs 012)
|
|
62
62
|
are a first-class cause, not an afterthought.
|
|
63
|
-
- 🔤 **
|
|
64
|
-
|
|
65
|
-
English.
|
|
63
|
+
- 🔤 **Speaks Arabic, Hebrew and Persian, right-to-left** — on a page whose
|
|
64
|
+
`<html lang>` is one of those, the overlay is in that language and laid out
|
|
65
|
+
right-to-left; everywhere else it is in English. Page data renders in its own direction in both, and invisible bidi
|
|
66
|
+
marks are shown instead of hidden.
|
|
66
67
|
- 🫧 **Zero production cost** — every code path is gated behind
|
|
67
68
|
`process.env.NODE_ENV`, and CI fails the build if the production bundle for
|
|
68
69
|
any entry point isn't tree-shaken to a no-op.
|
|
@@ -78,6 +79,7 @@ Supports **React 18 and 19**.
|
|
|
78
79
|
- [Install](#install)
|
|
79
80
|
- [Quick setup](#quick-setup)
|
|
80
81
|
- [What is a hydration mismatch](#what-is-a-hydration-mismatch)
|
|
82
|
+
- [Searching for this error?](#searching-for-this-error)
|
|
81
83
|
- [What why-hydration detects](#what-why-hydration-detects)
|
|
82
84
|
- [How detection works](#how-detection-works)
|
|
83
85
|
- [What you will see](#what-you-will-see)
|
|
@@ -237,6 +239,37 @@ resolved a different locale or timezone. React logs a warning to the console
|
|
|
237
239
|
when it detects this and, depending on what mismatched, may or may not repair
|
|
238
240
|
the DOM to match. `why-hydration` exists to make that warning actionable.
|
|
239
241
|
|
|
242
|
+
### Searching for this error?
|
|
243
|
+
|
|
244
|
+
If you landed here from one of these messages, this is the tool for it. Each
|
|
245
|
+
is quoted exactly as React prints it, and `why-hydration` recognizes every one.
|
|
246
|
+
With the [snapshot script](#quick-setup) in place it also names the component,
|
|
247
|
+
shows the server and client values side by side — including for the messages
|
|
248
|
+
that carry no values of their own — and tells you the likely cause and fix.
|
|
249
|
+
|
|
250
|
+
**React 19** (and Next.js 15+, which uses it):
|
|
251
|
+
|
|
252
|
+
```text
|
|
253
|
+
Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client.
|
|
254
|
+
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client.
|
|
255
|
+
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up.
|
|
256
|
+
In HTML, <div> cannot be a descendant of <p>. This will cause a hydration error.
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**React 18** (and Next.js 13–14):
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
Hydration failed because the initial UI does not match what was rendered on the server.
|
|
263
|
+
There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
|
|
264
|
+
There was an error while hydrating this Suspense boundary. Switched to client rendering.
|
|
265
|
+
Text content does not match server-rendered HTML.
|
|
266
|
+
Warning: Text content did not match. Server: "…" Client: "…"
|
|
267
|
+
Warning: Prop `className` did not match. Server: "…" Client: "…"
|
|
268
|
+
Warning: Expected server HTML to contain a matching <div> in <div>.
|
|
269
|
+
Warning: Did not expect server HTML to contain a <div> in <div>.
|
|
270
|
+
Warning: validateDOMNesting(...): <div> cannot appear as a descendant of <p>.
|
|
271
|
+
```
|
|
272
|
+
|
|
240
273
|
---
|
|
241
274
|
|
|
242
275
|
## What why-hydration detects
|
|
@@ -339,6 +372,7 @@ A page with no mismatches renders **nothing** — no overlay, no console output.
|
|
|
339
372
|
| **✕** on the hint bar | Closes just the "scroll to see all" hint; the panel stays. |
|
|
340
373
|
| `overlay={false}` | Never mounts it at all — `onReport` and the console output still work. |
|
|
341
374
|
| `overlay={{ position }}` | `bottom-right` (default), `bottom-left`, `top-right`, `top-left`. |
|
|
375
|
+
| `overlay={{ locale }}` | `'auto'` (default) follows `<html lang>`; `'en'`, `'ar'`, `'he'` or `'fa'` pins the language. See [RTL and Arabic support](#rtl-and-arabic-support). |
|
|
342
376
|
|
|
343
377
|
The overlay is a *view* over the collected reports, not the collector itself:
|
|
344
378
|
dismissing it does not stop detection, and `onReport` keeps firing. If a
|
|
@@ -368,24 +402,60 @@ there are more than fit:
|
|
|
368
402
|
|
|
369
403
|
## RTL and Arabic support
|
|
370
404
|
|
|
371
|
-
An Arabic app gets the same diagnosis quality as an English
|
|
372
|
-
both how the overlay renders and what the engine can actually
|
|
405
|
+
An Arabic, Hebrew or Persian app gets the same diagnosis quality as an English
|
|
406
|
+
one. That covers both how the overlay renders and what the engine can actually
|
|
407
|
+
detect.
|
|
373
408
|
|
|
374
409
|
### The overlay
|
|
375
410
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
411
|
+
**The overlay speaks the page's language.** On a page whose `<html lang>` is
|
|
412
|
+
Arabic, Hebrew or Persian, the whole panel is in that language and laid out
|
|
413
|
+
right-to-left: title, buttons, category names, the explanation, the fix, and
|
|
414
|
+
the scroll hint, with each language's plural rules.
|
|
415
|
+
|
|
416
|
+
| `<html lang>` | Overlay |
|
|
417
|
+
| ------------- | ------- |
|
|
418
|
+
| `ar`, `ar-SA`, `ar-EG`, … and regional varieties (`arz`, `ary`, …) | Arabic |
|
|
419
|
+
| `he`, `he-IL`, and the legacy `iw` | Hebrew |
|
|
420
|
+
| `fa`, `fa-IR`, `fa-AF`, and `prs` (Dari), `pes` | Persian |
|
|
421
|
+
| anything else — including other right-to-left languages such as Urdu | English, left-to-right |
|
|
422
|
+
|
|
423
|
+
An untranslated right-to-left page keeps the English panel left-to-right rather
|
|
424
|
+
than mirroring English text. Pin the language either way with
|
|
425
|
+
`overlay={{ locale: 'en' }}`, `'ar'`, `'he'` or `'fa'`.
|
|
426
|
+
|
|
427
|
+
What stays English: the console output, and `cause.explanation` /
|
|
428
|
+
`cause.suggestion` in the report `onReport` receives. Those are what people log,
|
|
429
|
+
grep and paste into issues, so they do not change with the page. Each report
|
|
430
|
+
also carries `cause.messageId` and `cause.params`, which is how the overlay
|
|
431
|
+
finds the translation — and how your own tooling can, too. The "Learn more"
|
|
432
|
+
links point at this README, which is in English, and say so.
|
|
433
|
+
|
|
434
|
+
**Page data renders in its own direction, in either panel.** A mismatched value
|
|
435
|
+
can be in any script, so each value cell takes its direction from its own first
|
|
436
|
+
strong character. `السعر: ١٬٤٠٠ د.ك.` reads right-to-left with its full stop at
|
|
437
|
+
the end even inside the English panel, and `Price: 1,400 KWD.` reads
|
|
438
|
+
left-to-right inside the Arabic one. Selectors, component names, file paths and
|
|
439
|
+
attribute names are code, so they are always isolated left-to-right — otherwise
|
|
440
|
+
a right-to-left line would mirror their brackets and reorder their segments.
|
|
441
|
+
Class tokens and other values quoted inside a sentence are isolated one by one,
|
|
442
|
+
so two Arabic tokens cannot swap places. Arabic is drawn in a proportional face
|
|
443
|
+
(a monospace fallback renders it with its letters disconnected), and Arabic
|
|
444
|
+
labels are never letter-spaced or uppercased, which breaks their joining.
|
|
445
|
+
|
|
446
|
+
**What differs is what you see.** Value cells preserve whitespace, so a
|
|
447
|
+
whitespace-only mismatch no longer looks like two identical strings. Invisible
|
|
448
|
+
characters that change layout — `LRM`, `RLM`, `ALM`, the bidi embeddings and
|
|
449
|
+
isolates, zero-width space, BOM — are drawn as small labelled badges rather than
|
|
450
|
+
applied, so the [bidi-mark case](#cause-locale-format) shows a visible
|
|
451
|
+
difference instead of two cells that look the same.
|
|
452
|
+
|
|
453
|
+
**It is isolated from the page.** The panel lives in a Shadow DOM, and its
|
|
454
|
+
direction is set inside it, so nothing in the host page's CSS can flip it — the
|
|
455
|
+
`all` shorthand deliberately excludes `direction` (per spec), so
|
|
456
|
+
`:host { all: initial }` alone would not be enough. The panel also declares its
|
|
457
|
+
own `lang`, so a screen reader voices it in its own language: before this, an
|
|
458
|
+
English panel on an Arabic page was read aloud with an Arabic voice.
|
|
389
459
|
|
|
390
460
|
### Detection
|
|
391
461
|
|
|
@@ -460,6 +530,14 @@ Wrap your app (or, for Next.js, use the `why-hydration/next` re-export).
|
|
|
460
530
|
| `classify` | `Classifier[]` | `[]` | Custom classification rules, run **before** the built-in ones. |
|
|
461
531
|
| `maxReports` | `number` | `25` | Cap on the number of unique reports collected per page load. |
|
|
462
532
|
|
|
533
|
+
Props can change while the app is running, and take effect without a reload:
|
|
534
|
+
a new `onReport` receives the next report; a new `overlay` (switched on or off,
|
|
535
|
+
or a different `position` or `locale`) rebuilds the panel with the reports so
|
|
536
|
+
far; new `ignore`, `classify` or `maxReports` apply to everything reported from
|
|
537
|
+
then on. Reports already made are kept as they were. Inline values that are
|
|
538
|
+
re-created on every render, such as `overlay={{ position: 'top-left' }}`, do
|
|
539
|
+
not rebuild anything unless they actually change.
|
|
540
|
+
|
|
463
541
|
### `createHydrationInspector(options)`
|
|
464
542
|
|
|
465
543
|
For Vite/CRA/Remix, where you call `hydrateRoot` yourself. Accepts the same
|
|
@@ -490,6 +568,7 @@ effect setup → cleanup → setup.
|
|
|
490
568
|
```ts
|
|
491
569
|
interface OverlayOptions {
|
|
492
570
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; // default 'bottom-right'
|
|
571
|
+
locale?: 'auto' | 'en' | 'ar' | 'he' | 'fa'; // default 'auto' — follows <html lang>
|
|
493
572
|
}
|
|
494
573
|
```
|
|
495
574
|
|
|
@@ -538,9 +617,11 @@ interface HydrationReport {
|
|
|
538
617
|
cause: {
|
|
539
618
|
category: HydrationCauseCategory; // one of the categories below, or "unknown"
|
|
540
619
|
confidence: number; // 0–1
|
|
541
|
-
explanation: string;
|
|
542
|
-
suggestion: string;
|
|
620
|
+
explanation: string; // always English
|
|
621
|
+
suggestion: string; // always English
|
|
543
622
|
docsUrl?: string;
|
|
623
|
+
messageId?: string; // which message produced the text, e.g. "attribute-mismatch.class"
|
|
624
|
+
params?: Record<string, string | string[]>; // the values in it, e.g. { added: ['wide'], removed: [] }
|
|
544
625
|
};
|
|
545
626
|
raw?: { reactMessage?: string }; // the original React console message, if any
|
|
546
627
|
}
|
|
@@ -662,6 +743,16 @@ JavaScript.
|
|
|
662
743
|
A node was moved or ejected because the markup is invalid HTML (e.g. a `<div>`
|
|
663
744
|
inside a `<p>`, or a nested `<a>`). The browser repairs the server-rendered DOM
|
|
664
745
|
so it no longer matches what React expects.
|
|
746
|
+
|
|
747
|
+
It is reported **once**, at the element that is misplaced. The browser repairs
|
|
748
|
+
the server's HTML while parsing it, but React builds the client DOM node by
|
|
749
|
+
node, so nothing repairs that side — and compared as-is, one invalid `<div>`
|
|
750
|
+
used to surface as three unrelated reports. The client side is now put through
|
|
751
|
+
the same parser repair before comparing, so the nesting shows up as a single
|
|
752
|
+
report, and any real text difference inside it still shows up as its own.
|
|
753
|
+
Covered: block elements inside `<p>`, nested `<a>`, `<button>` and `<form>`,
|
|
754
|
+
and table content outside its section or row (which the parser moves out of
|
|
755
|
+
the whole table).
|
|
665
756
|
**Fix:** correct the markup validity — block elements cannot live inside
|
|
666
757
|
`<p>`, anchors cannot nest, etc.
|
|
667
758
|
**Reference:** [MDN — `<p>` (permitted content)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p).
|
|
@@ -701,6 +792,13 @@ drive the visual change with a CSS media query instead of a JS class toggle.
|
|
|
701
792
|
A mismatch was detected but didn't match any of the rules above. The report
|
|
702
793
|
still shows the exact server vs. client values and node path so you can
|
|
703
794
|
diagnose it directly.
|
|
795
|
+
|
|
796
|
+
React also logs a bare "hydration failed" message that names no node at all.
|
|
797
|
+
When the DOM diff or another React message has already located the mismatch,
|
|
798
|
+
that message adds nothing and is not shown. When it is the *only* signal —
|
|
799
|
+
typically because the snapshot script is missing — it is reported once, with an
|
|
800
|
+
explanation that says React did not name the node and a fix that points at the
|
|
801
|
+
snapshot script, rather than asking you to inspect values that do not exist.
|
|
704
802
|
**Fix:** compare the two values — the cause is usually one of the categories
|
|
705
803
|
above. If you find a reliable signal for it, add a custom rule via the
|
|
706
804
|
`classify` option (see [Contributing](#contributing)).
|
|
@@ -729,9 +827,12 @@ above. If you find a reliable signal for it, add a custom rule via the
|
|
|
729
827
|
budget (`npm run size`) against a real production bundle built with webpack
|
|
730
828
|
and terser — the same toolchain Next.js and CRA use for production — and
|
|
731
829
|
fails the build if the tree-shaken output for any entry point isn't reduced
|
|
732
|
-
to a near-empty stub.
|
|
733
|
-
|
|
734
|
-
|
|
830
|
+
to a near-empty stub. The budgets sit just above today's output, so even a
|
|
831
|
+
small leak fails CI. Verified independently against Rollup, which is what
|
|
832
|
+
Vite uses for production builds: **99 B** minified + gzipped under webpack,
|
|
833
|
+
and **156 B** minified (not gzipped) under Rollup — versus ~59 KB minified
|
|
834
|
+
for the same entry built for development. The Arabic translations exist only
|
|
835
|
+
in that development build.
|
|
735
836
|
- `<HydrationSnapshotScript>` also renders `null` outside development, so no
|
|
736
837
|
snapshot script is emitted into your production HTML.
|
|
737
838
|
|
|
@@ -788,9 +889,11 @@ snapshot script is present in `<head>` and runs before your app's hydration
|
|
|
788
889
|
script. Without the snapshot script, the tool still reports mismatches it can
|
|
789
890
|
parse from React's own console warning, but loses the precise DOM-level diff.
|
|
790
891
|
If you passed `roots`, check the console for a
|
|
791
|
-
`[why-hydration] Skipping root:` warning
|
|
792
|
-
|
|
793
|
-
|
|
892
|
+
`[why-hydration] Skipping root:` warning. There are three: a selector that is
|
|
893
|
+
not valid CSS, a root with no captured server HTML (it can never produce a
|
|
894
|
+
DOM-level report), and a selector that matches nothing on the page — usually a
|
|
895
|
+
typo. The last is only raised once the settling window has closed, so a root
|
|
896
|
+
rendered a moment after hydration is not reported by mistake.
|
|
794
897
|
|
|
795
898
|
**Does it work under `<React.StrictMode>`?** Yes. Strict Mode runs every mount
|
|
796
899
|
effect setup → cleanup → setup in development, which tears the inspector down
|
|
@@ -802,6 +905,11 @@ to the rebuilt inspector rather than lost.
|
|
|
802
905
|
suspend `requestAnimationFrame` on hidden pages, so scheduling races a frame
|
|
803
906
|
against a 50 ms timer and no longer depends on the page being painted.
|
|
804
907
|
|
|
908
|
+
**The component name in a report was a tag like `<p>`.** Fixed. React's
|
|
909
|
+
component stacks list host elements first (`at p`, `at span`), and those were
|
|
910
|
+
being taken as the component. The nearest component you wrote is used instead,
|
|
911
|
+
and when there is none the report names no component rather than a tag.
|
|
912
|
+
|
|
805
913
|
**The "Learn more →" link 404s.** The links point at this README on GitHub
|
|
806
914
|
(`github.com/razan-aboushi/why-hydration#cause-…`). If you've forked the
|
|
807
915
|
package under a different name or repository, update `DOCS_BASE` in
|
|
@@ -823,16 +931,28 @@ production. It's dev-only, runs a bounded diff across roughly 1.5 seconds
|
|
|
823
931
|
right after hydration, then stops.
|
|
824
932
|
|
|
825
933
|
**Does it work with `<html dir="rtl">`?** Yes — see
|
|
826
|
-
[RTL and Arabic support](#rtl-and-arabic-support). The
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
934
|
+
[RTL and Arabic support](#rtl-and-arabic-support). The panel's language follows
|
|
935
|
+
`<html lang>`, not `dir`: an Arabic, Hebrew or Persian page gets the panel in
|
|
936
|
+
that language, right-to-left, and a right-to-left page in another language gets
|
|
937
|
+
the English left-to-right one. Detection covers Arabic-script formatting
|
|
938
|
+
mismatches on both sides, not just Arabic-vs-Latin.
|
|
939
|
+
|
|
940
|
+
**I want the English panel on my Arabic site (or the reverse).** Pass
|
|
941
|
+
`overlay={{ locale: 'en' }}` (or `'ar'`, `'he'`, `'fa'`). The console and
|
|
942
|
+
`onReport` are English either way.
|
|
943
|
+
|
|
944
|
+
**I changed a `<HydrationInspector>` prop and nothing happened.** Fixed —
|
|
945
|
+
props used to be read once, at mount. They now apply while the app runs; see
|
|
946
|
+
[`<HydrationInspector>`](#hydrationinspector).
|
|
947
|
+
|
|
948
|
+
**My Arabic app reports a mismatch between two values that look the same.** They
|
|
949
|
+
differ by invisible bidirectional control characters — `Intl` adds LRM/RLM/isolate
|
|
832
950
|
marks around numbers and dates in RTL locales, and Node's ICU and the browser's
|
|
833
951
|
ICU do not always agree on which. The report names this explicitly under
|
|
834
|
-
[`locale-format`](#cause-locale-format)
|
|
835
|
-
|
|
952
|
+
[`locale-format`](#cause-locale-format), and the overlay draws each invisible
|
|
953
|
+
mark as a small labelled badge (`RLM`, `LRM`, …) in the value cell, so you can
|
|
954
|
+
see which side has it. Format the value in one place and pass the string down,
|
|
955
|
+
or add `suppressHydrationWarning` if the marks are harmless.
|
|
836
956
|
|
|
837
957
|
---
|
|
838
958
|
|