diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js
index 0c4edff51a490..f578748e923d2 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js
@@ -182,7 +182,8 @@ describe('ReactDOMFizzForm', () => {
ReactDOMClient.hydrateRoot(container, );
});
}).toErrorDev(
- 'Prop `action` did not match. Server: "function" Client: "action"',
+ "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.",
+ {withoutStack: true},
);
});
@@ -344,7 +345,12 @@ describe('ReactDOMFizzForm', () => {
await act(async () => {
root = ReactDOMClient.hydrateRoot(container, );
});
- }).toErrorDev(['Prop `formTarget` did not match.']);
+ }).toErrorDev(
+ [
+ "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.",
+ ],
+ {withoutStack: true},
+ );
await act(async () => {
root.render();
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index 33b2b322de873..1caaeca68d293 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -47,6 +47,15 @@ let waitForPaint;
let clientAct;
let streamingContainer;
+function normalizeError(msg) {
+ // Take the first sentence to make it easier to assert on.
+ const idx = msg.indexOf('.');
+ if (idx > -1) {
+ return msg.slice(0, idx + 1);
+ }
+ return msg;
+}
+
describe('ReactDOMFizzServer', () => {
beforeEach(() => {
jest.resetModules();
@@ -2391,7 +2400,9 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Log recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Log recoverable error: ' + normalizeError(error.message),
+ );
},
});
@@ -2399,18 +2410,12 @@ describe('ReactDOMFizzServer', () => {
// The first paint switches to client rendering due to mismatch
await waitForPaint([
'client',
- 'Log recoverable error: Hydration failed because the initial ' +
- 'UI does not match what was rendered on the server.',
- 'Log recoverable error: There was an error while hydrating. ' +
- 'Because the error happened outside of a Suspense boundary, the ' +
- 'entire root will switch to client rendering.',
+ "Log recoverable error: Hydration failed because the server rendered HTML didn't match the client.",
+ 'Log recoverable error: There was an error while hydrating.',
]);
}).toErrorDev(
[
'Warning: An error occurred during hydration. The server HTML was replaced with client content.',
- 'Warning: Expected server HTML to contain a matching
in the root.\n' +
- ' in div (at **)\n' +
- ' in App (at **)',
],
{withoutStack: 1},
);
@@ -2474,7 +2479,9 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Log recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Log recoverable error: ' + normalizeError(error.message),
+ );
},
});
@@ -2483,18 +2490,12 @@ describe('ReactDOMFizzServer', () => {
// The first paint switches to client rendering due to mismatch
await waitForPaint([
'client',
- 'Log recoverable error: Hydration failed because the initial ' +
- 'UI does not match what was rendered on the server.',
- 'Log recoverable error: There was an error while hydrating. ' +
- 'Because the error happened outside of a Suspense boundary, the ' +
- 'entire root will switch to client rendering.',
+ "Log recoverable error: Hydration failed because the server rendered HTML didn't match the client.",
+ 'Log recoverable error: There was an error while hydrating.',
]);
}).toErrorDev(
[
'Warning: An error occurred during hydration. The server HTML was replaced with client content',
- 'Warning: Expected server HTML to contain a matching
in the root.\n' +
- ' in div (at **)\n' +
- ' in App (at **)',
],
{withoutStack: 1},
);
@@ -2557,7 +2558,7 @@ describe('ReactDOMFizzServer', () => {
isClient = true;
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
@@ -2567,9 +2568,7 @@ describe('ReactDOMFizzServer', () => {
await waitForAll([
'Yay!',
'Hydration error',
- 'There was an error while hydrating. Because the error happened ' +
- 'outside of a Suspense boundary, the entire root will switch ' +
- 'to client rendering.',
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
'An error occurred during hydration. The server HTML was replaced',
@@ -2739,7 +2738,7 @@ describe('ReactDOMFizzServer', () => {
isClient = true;
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
@@ -2748,7 +2747,7 @@ describe('ReactDOMFizzServer', () => {
await waitForAll([
'Yay!',
'Hydration error',
- 'There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ 'There was an error while hydrating this Suspense boundary.',
]);
expect(getVisibleChildren(container)).toEqual(
@@ -3194,7 +3193,7 @@ describe('ReactDOMFizzServer', () => {
isClient = true;
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
@@ -3202,8 +3201,7 @@ describe('ReactDOMFizzServer', () => {
// to client rendering.
await waitForAll([
'Hydration error',
- 'There was an error while hydrating this Suspense boundary. Switched ' +
- 'to client rendering.',
+ 'There was an error while hydrating this Suspense boundary.',
]);
expect(getVisibleChildren(container)).toEqual(
@@ -3263,7 +3261,9 @@ describe('ReactDOMFizzServer', () => {
const root = ReactDOMClient.createRoot(container, {
onRecoverableError(error) {
- Scheduler.log('Logged a recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged a recoverable error: ' + normalizeError(error.message),
+ );
},
});
React.startTransition(() => {
@@ -3339,7 +3339,9 @@ describe('ReactDOMFizzServer', () => {
isClient = true;
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
@@ -3349,11 +3351,11 @@ describe('ReactDOMFizzServer', () => {
'Logged recoverable error: Hydration error',
'Logged recoverable error: There was an error while hydrating this ' +
- 'Suspense boundary. Switched to client rendering.',
+ 'Suspense boundary.',
'Logged recoverable error: Hydration error',
'Logged recoverable error: There was an error while hydrating this ' +
- 'Suspense boundary. Switched to client rendering.',
+ 'Suspense boundary.',
]);
});
@@ -4395,7 +4397,9 @@ describe('ReactDOMFizzServer', () => {
const [ClientApp, clientResolve] = makeApp();
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([]);
@@ -4471,7 +4475,9 @@ describe('ReactDOMFizzServer', () => {
const [ClientApp, clientResolve] = makeApp();
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([]);
@@ -4486,14 +4492,10 @@ describe('ReactDOMFizzServer', () => {
// Now that the boundary resolves to it's children the hydration completes and discovers that there is a mismatch requiring
// client-side rendering.
await clientResolve();
- await expect(async () => {
- await waitForAll([
- 'Logged recoverable error: Text content does not match server-rendered HTML.',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
- ]);
- }).toErrorDev(
- 'Warning: Text content did not match. Server: "initial" Client: "replaced',
- );
+ await waitForAll([
+ "Logged recoverable error: Hydration failed because the server rendered HTML didn't match the client.",
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
+ ]);
expect(getVisibleChildren(container)).toEqual(
A
@@ -4539,12 +4541,14 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([
- 'Logged recoverable error: Text content does not match server-rendered HTML.',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ "Logged recoverable error: Hydration failed because the server rendered HTML didn't match the client.",
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
]);
expect(getVisibleChildren(container)).toEqual(
@@ -4556,21 +4560,7 @@ describe('ReactDOMFizzServer', () => {
);
await waitForAll([]);
- if (__DEV__) {
- expect(mockError.mock.calls.length).toBe(1);
- expect(mockError.mock.calls[0]).toEqual([
- 'Warning: Text content did not match. Server: "%s" Client: "%s"%s',
- 'initial',
- 'replaced',
- '\n' +
- ' in h2 (at **)\n' +
- ' in Suspense (at **)\n' +
- ' in div (at **)\n' +
- ' in App (at **)',
- ]);
- } else {
- expect(mockError.mock.calls.length).toBe(0);
- }
+ expect(mockError.mock.calls.length).toBe(0);
} finally {
console.error = originalConsoleError;
}
@@ -4626,12 +4616,14 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([
'Logged recoverable error: uh oh',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
]);
expect(getVisibleChildren(container)).toEqual(
@@ -4713,7 +4705,9 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([
@@ -4722,7 +4716,7 @@ describe('ReactDOMFizzServer', () => {
// onRecoverableError because the UI recovered without surfacing the
// error to the user.
'Logged recoverable error: first error',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
]);
expect(mockError.mock.calls).toEqual([]);
mockError.mockClear();
@@ -4830,7 +4824,9 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll(['suspending']);
@@ -4847,7 +4843,7 @@ describe('ReactDOMFizzServer', () => {
await waitForAll([
'throwing: first error',
'Logged recoverable error: first error',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
]);
expect(getVisibleChildren(container)).toEqual(
@@ -4954,14 +4950,16 @@ describe('ReactDOMFizzServer', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log('Logged recoverable error: ' + error.message);
+ Scheduler.log(
+ 'Logged recoverable error: ' + normalizeError(error.message),
+ );
},
});
await waitForAll([
'throwing: first error',
'suspending',
'Logged recoverable error: first error',
- 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.',
+ 'Logged recoverable error: There was an error while hydrating this Suspense boundary.',
]);
expect(mockError.mock.calls).toEqual([]);
mockError.mockClear();
@@ -6341,13 +6339,7 @@ describe('ReactDOMFizzServer', () => {
});
await expect(async () => {
await waitForAll([]);
- }).toErrorDev(
- [
- 'Expected server HTML to contain a matching in the root',
- 'An error occurred during hydration',
- ],
- {withoutStack: 1},
- );
+ }).toErrorDev(['An error occurred during hydration'], {withoutStack: 1});
expect(errors.length).toEqual(2);
expect(getVisibleChildren(container)).toEqual();
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
index 5fa719e3d36cc..36c3d4f804dd3 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
@@ -24,6 +24,15 @@ let hasErrored = false;
let fatalError = undefined;
let waitForAll;
+function normalizeError(msg) {
+ // Take the first sentence to make it easier to assert on.
+ const idx = msg.indexOf('.');
+ if (idx > -1) {
+ return msg.slice(0, idx + 1);
+ }
+ return msg;
+}
+
describe('ReactDOMFizzServerHydrationWarning', () => {
beforeEach(() => {
jest.resetModules();
@@ -156,7 +165,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
// Don't miss a hydration error. There should be none.
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await waitForAll([]);
@@ -196,7 +205,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await waitForAll([]);
@@ -237,17 +246,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Expected server HTML to contain a matching in ',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -282,7 +290,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
const root = ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await waitForAll([]);
@@ -326,17 +334,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Did not expect server HTML to contain the text node "Server" in ',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -374,17 +381,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Expected server HTML to contain a matching text node for "Client" in .',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -425,17 +431,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Did not expect server HTML to contain the text node "Server" in .',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -474,17 +479,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Expected server HTML to contain a matching text node for "Client" in .',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -527,7 +531,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await waitForAll([]);
@@ -564,7 +568,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await waitForAll([]);
@@ -597,17 +601,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Expected server HTML to contain a matching
in
.',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -643,17 +646,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
ReactDOMClient.hydrateRoot(container, , {
onRecoverableError(error) {
- Scheduler.log(error.message);
+ Scheduler.log(normalizeError(error.message));
},
});
await expect(async () => {
await waitForAll([
- 'Hydration failed because the initial UI does not match what was rendered on the server.',
- 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
+ "Hydration failed because the server rendered HTML didn't match the client.",
+ 'There was an error while hydrating.',
]);
}).toErrorDev(
[
- 'Did not expect server HTML to contain a
in
.',
'An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
index fae926c622564..7b8e27654c6a6 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
@@ -6480,7 +6480,6 @@ body {
await waitForAll([]);
}).toErrorDev(
[
- 'Warning: Text content did not match. Server: "server" Client: "client"',
'Warning: An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
@@ -8270,7 +8269,6 @@ background-color: green;
await waitForAll([]);
}).toErrorDev(
[
- 'Warning: Text content did not match. Server: "server" Client: "client"',
'Warning: An error occurred during hydration. The server HTML was replaced with client content.',
],
{withoutStack: 1},
diff --git a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js
index ef5507744b4ed..c134ab973d58c 100644
--- a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js
@@ -82,12 +82,19 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Text content did not match. Server: "server" Client: "client"
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
"Warning: An error occurred during hydration. The server HTML was replaced with client content.",
- "Caught [Text content does not match server-rendered HTML.]",
+ "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ]",
"Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
]
`);
@@ -109,11 +116,19 @@ describe('ReactDOMServerHydration', () => {
/* eslint-disable no-irregular-whitespace */
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Text content did not match. Server: "This markup contains an nbsp entity: server text" Client: "This markup contains an nbsp entity: client text"
- in div (at **)
- in Mismatch (at **)",
"Warning: An error occurred during hydration. The server HTML was replaced with client content.",
- "Caught [Text content does not match server-rendered HTML.]",
+ "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ]",
"Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
]
`);
@@ -138,10 +153,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Prop \`dangerouslySetInnerHTML\` did not match. Server: {"__html":"server"} Client: {"__html":"client"}
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -162,10 +185,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Prop \`className\` did not match. Server: "child server" Client: "child client"
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -185,10 +216,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Prop \`tabIndex\` did not match. Server: null Client: 1
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -208,10 +247,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Extra attribute from the server: tabindex
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -231,10 +278,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Prop \`tabIndex\` did not match. Server: null Client: 1
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -255,10 +310,18 @@ describe('ReactDOMServerHydration', () => {
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
[
- "Warning: Prop \`style\` did not match. Server: {"opacity":"0"} Client: {"opacity":1}
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
+ "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ",
]
`);
});
@@ -276,16 +339,23 @@ describe('ReactDOMServerHydration', () => {
);
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
- [
- "Warning: Expected server HTML to contain a matching in
.
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
- "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
- "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]",
- "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
- ]
- `);
+ [
+ "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
+ "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ]",
+ "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
+ ]
+ `);
});
// @gate __DEV__
@@ -300,16 +370,23 @@ describe('ReactDOMServerHydration', () => {
);
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
- [
- "Warning: Expected server HTML to contain a matching in
.
- in header (at **)
- in div (at **)
- in Mismatch (at **)",
- "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
- "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]",
- "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
- ]
- `);
+ [
+ "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
+ "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ]",
+ "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
+ ]
+ `);
});
// @gate __DEV__
@@ -324,16 +401,23 @@ describe('ReactDOMServerHydration', () => {
);
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
- [
- "Warning: Expected server HTML to contain a matching in
.
- in main (at **)
- in div (at **)
- in Mismatch (at **)",
- "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
- "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]",
- "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
- ]
- `);
+ [
+ "Warning: An error occurred during hydration. The server HTML was replaced with client content.",
+ "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
+
+ - A server/client branch \`if (typeof window !== 'undefined')\`.
+ - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
+ - Date formatting in a user's locale which doesn't match the server.
+ - External changing data without sending a snapshot of it along with the HTML.
+ - Invalid HTML tag nesting.
+
+ It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
+
+ https://react.dev/link/hydration-mismatch
+ ]",
+ "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]",
+ ]
+ `);
});
// @gate __DEV__
@@ -348,16 +432,23 @@ describe('ReactDOMServerHydration', () => {
);
}
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
- [
- "Warning: Expected server HTML to contain a matching