why-hydration 0.2.0 → 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 +66 -0
- package/README.md +87 -23
- package/dist/chunk-CER5NQRR.js +1333 -0
- package/dist/chunk-CER5NQRR.js.map +1 -0
- package/dist/{chunk-3OAI2ZHY.js → chunk-KP4S6CRL.js} +339 -181
- package/dist/chunk-KP4S6CRL.js.map +1 -0
- package/dist/{chunk-KX6G7I4Y.cjs → chunk-QFHUKJ5K.cjs} +339 -181
- 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 +19 -19
- package/dist/index.d.cts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +1 -1
- package/dist/next/index.cjs +4 -4
- package/dist/next/index.js +2 -2
- package/dist/react.cjs +4 -4
- package/dist/react.d.cts +4 -3
- package/dist/react.d.ts +4 -3
- package/dist/react.js +2 -2
- package/package.json +22 -4
- package/dist/chunk-3OAI2ZHY.js.map +0 -1
- package/dist/chunk-KX6G7I4Y.cjs.map +0 -1
- package/dist/chunk-MK3TYHKM.js +0 -966
- package/dist/chunk-MK3TYHKM.js.map +0 -1
- package/dist/chunk-R7IWOK5M.cjs +0 -989
- package/dist/chunk-R7IWOK5M.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
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
|
+
|
|
3
69
|
## 0.2.0
|
|
4
70
|
|
|
5
71
|
### Minor 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,9 @@ 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
|
-
- 🔤 **Speaks Arabic, right-to-left** — on
|
|
64
|
-
the overlay is in
|
|
65
|
-
English. Page data renders in its own direction in both, and invisible bidi
|
|
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
66
|
marks are shown instead of hidden.
|
|
67
67
|
- 🫧 **Zero production cost** — every code path is gated behind
|
|
68
68
|
`process.env.NODE_ENV`, and CI fails the build if the production bundle for
|
|
@@ -79,6 +79,7 @@ Supports **React 18 and 19**.
|
|
|
79
79
|
- [Install](#install)
|
|
80
80
|
- [Quick setup](#quick-setup)
|
|
81
81
|
- [What is a hydration mismatch](#what-is-a-hydration-mismatch)
|
|
82
|
+
- [Searching for this error?](#searching-for-this-error)
|
|
82
83
|
- [What why-hydration detects](#what-why-hydration-detects)
|
|
83
84
|
- [How detection works](#how-detection-works)
|
|
84
85
|
- [What you will see](#what-you-will-see)
|
|
@@ -238,6 +239,37 @@ resolved a different locale or timezone. React logs a warning to the console
|
|
|
238
239
|
when it detects this and, depending on what mismatched, may or may not repair
|
|
239
240
|
the DOM to match. `why-hydration` exists to make that warning actionable.
|
|
240
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
|
+
|
|
241
273
|
---
|
|
242
274
|
|
|
243
275
|
## What why-hydration detects
|
|
@@ -340,7 +372,7 @@ A page with no mismatches renders **nothing** — no overlay, no console output.
|
|
|
340
372
|
| **✕** on the hint bar | Closes just the "scroll to see all" hint; the panel stays. |
|
|
341
373
|
| `overlay={false}` | Never mounts it at all — `onReport` and the console output still work. |
|
|
342
374
|
| `overlay={{ position }}` | `bottom-right` (default), `bottom-left`, `top-right`, `top-left`. |
|
|
343
|
-
| `overlay={{ locale }}` | `'auto'` (default) follows `<html lang>`; `'en'` or `'
|
|
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). |
|
|
344
376
|
|
|
345
377
|
The overlay is a *view* over the collected reports, not the collector itself:
|
|
346
378
|
dismissing it does not stop detection, and `onReport` keeps firing. If a
|
|
@@ -370,19 +402,27 @@ there are more than fit:
|
|
|
370
402
|
|
|
371
403
|
## RTL and Arabic support
|
|
372
404
|
|
|
373
|
-
An Arabic app gets the same diagnosis quality as an English
|
|
374
|
-
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.
|
|
375
408
|
|
|
376
409
|
### The overlay
|
|
377
410
|
|
|
378
411
|
**The overlay speaks the page's language.** On a page whose `<html lang>` is
|
|
379
|
-
Arabic
|
|
412
|
+
Arabic, Hebrew or Persian, the whole panel is in that language and laid out
|
|
380
413
|
right-to-left: title, buttons, category names, the explanation, the fix, and
|
|
381
|
-
the scroll hint, with
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
`
|
|
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'`.
|
|
386
426
|
|
|
387
427
|
What stays English: the console output, and `cause.explanation` /
|
|
388
428
|
`cause.suggestion` in the report `onReport` receives. Those are what people log,
|
|
@@ -490,6 +530,14 @@ Wrap your app (or, for Next.js, use the `why-hydration/next` re-export).
|
|
|
490
530
|
| `classify` | `Classifier[]` | `[]` | Custom classification rules, run **before** the built-in ones. |
|
|
491
531
|
| `maxReports` | `number` | `25` | Cap on the number of unique reports collected per page load. |
|
|
492
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
|
+
|
|
493
541
|
### `createHydrationInspector(options)`
|
|
494
542
|
|
|
495
543
|
For Vite/CRA/Remix, where you call `hydrateRoot` yourself. Accepts the same
|
|
@@ -520,7 +568,7 @@ effect setup → cleanup → setup.
|
|
|
520
568
|
```ts
|
|
521
569
|
interface OverlayOptions {
|
|
522
570
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; // default 'bottom-right'
|
|
523
|
-
locale?: 'auto' | 'en' | 'ar'; // default 'auto' — follows <html lang>
|
|
571
|
+
locale?: 'auto' | 'en' | 'ar' | 'he' | 'fa'; // default 'auto' — follows <html lang>
|
|
524
572
|
}
|
|
525
573
|
```
|
|
526
574
|
|
|
@@ -695,6 +743,16 @@ JavaScript.
|
|
|
695
743
|
A node was moved or ejected because the markup is invalid HTML (e.g. a `<div>`
|
|
696
744
|
inside a `<p>`, or a nested `<a>`). The browser repairs the server-rendered DOM
|
|
697
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).
|
|
698
756
|
**Fix:** correct the markup validity — block elements cannot live inside
|
|
699
757
|
`<p>`, anchors cannot nest, etc.
|
|
700
758
|
**Reference:** [MDN — `<p>` (permitted content)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p).
|
|
@@ -831,9 +889,11 @@ snapshot script is present in `<head>` and runs before your app's hydration
|
|
|
831
889
|
script. Without the snapshot script, the tool still reports mismatches it can
|
|
832
890
|
parse from React's own console warning, but loses the precise DOM-level diff.
|
|
833
891
|
If you passed `roots`, check the console for a
|
|
834
|
-
`[why-hydration] Skipping root:` warning
|
|
835
|
-
|
|
836
|
-
|
|
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.
|
|
837
897
|
|
|
838
898
|
**Does it work under `<React.StrictMode>`?** Yes. Strict Mode runs every mount
|
|
839
899
|
effect setup → cleanup → setup in development, which tears the inspector down
|
|
@@ -872,14 +932,18 @@ right after hydration, then stops.
|
|
|
872
932
|
|
|
873
933
|
**Does it work with `<html dir="rtl">`?** Yes — see
|
|
874
934
|
[RTL and Arabic support](#rtl-and-arabic-support). The panel's language follows
|
|
875
|
-
`<html lang>`, not `dir`: an Arabic page gets the
|
|
876
|
-
and a right-to-left page in another language gets
|
|
877
|
-
one. Detection covers Arabic-script formatting
|
|
878
|
-
just Arabic-vs-Latin.
|
|
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.
|
|
879
939
|
|
|
880
940
|
**I want the English panel on my Arabic site (or the reverse).** Pass
|
|
881
|
-
`overlay={{ locale: 'en' }}` (or `'ar'`). The console and
|
|
882
|
-
either way.
|
|
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).
|
|
883
947
|
|
|
884
948
|
**My Arabic app reports a mismatch between two values that look the same.** They
|
|
885
949
|
differ by invisible bidirectional control characters — `Intl` adds LRM/RLM/isolate
|