The ESLint no-invalid-jsx-nesting rule, from eslint-plugin-validate-jsx-nesting, reports HTML nesting the browser would repair, such as a <p> inside a <p>, when the child tag is written directly inside the parent. hydration-proof's no-invalid-interactive-nesting looks through fragments, conditions and lists and at any depth, and explains the hydration error each case causes.
At a glance
no-invalid-jsx-nesting | no-invalid-interactive-nesting | |
|---|---|---|
| Checks | A tag and its direct parent tag | A tag and every enclosing tag in the same component |
Through fragments, &&, ? :, arrays, .map() | No | Yes |
<div> inside <span> inside <p> | Not reported | Reported |
<a> in <a>, <button> in <button>, <form> in <form> | Direct children | At any depth |
Interactive content in <a> or <button> | Not checked | input, select, textarea, label, iframe and more |
| Table structure | <tr> outside a table section, <td> outside <tr>, and more | <tr> directly in <table>, <td> directly in a table section |
Headings in headings, <li> in <li>, <select> and <head> content | Checked | Not checked |
| Across components | No | No; hydration-proof test finds those |
| Server Components | Checked | Checked, even with the next preset |
| Message | Invalid HTML nesting: <div> can not be child of <p> | What the parser does and how hydration fails |
| Latest release | 0.1.1, April 2023 | 1.0 |
eslint-plugin-react, the other common React linter, has no rule for DOM nesting.
What ESLint no-invalid-jsx-nesting does well
The plugin has one rule and does it simply:
- A broad list of content rules. It uses the validate-html-nesting library, which knows the only valid children of
<table>,<tr>,<select>,<head>and<colgroup>, elements such as<textarea>and<option>that take no element children, and pairs like a heading in a heading or<li>in<li>. Some of these are outside what hydration-proof's rule checks. - Framework agnostic. It works on any JSX, not only React.
- Small. Its only dependency is validate-html-nesting.
It has about 42,000 weekly downloads on npm.
Where hydration-proof differs
It looks past the direct parent. no-invalid-jsx-nesting compares a tag with its parent only when both are HTML tags written next to each other. These all break hydration and are not reported by it:
export function Intro({
items,
showMore,
}: {
items: string[];
showMore: boolean;
}) {
return (
<p>
Welcome.
{/* Inside a condition: the parent of <div> is not the <p> tag. */}
{showMore && <div>Read more below.</div>}
{/* One level deeper: the browser still closes the <p>. */}
<span>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</span>
</p>
);
}no-invalid-interactive-nesting follows fragments, &&, ? :, arrays and .map() callbacks, and walks every enclosing tag in the component, stopping where the HTML parser would. It reports both cases above.
It covers interactive content. A <button>, <input> or <select> inside a link, or a link inside a button, is reported with the reason. See HP3002 for what the CLI reports.
It explains the hydration error. Each message says what the browser does with the markup and how that breaks hydration, for example that the browser closes the <p> before the <div>, so the DOM no longer matches what React rendered.
It is part of a hydration rule set. The same plugin has 14 more rules for the clock, random values, locale, timezone, storage and browser APIs (all rules).
Neither linter sees nesting across components: <p><Card /></p> where Card renders a <div>. hydration-proof test checks the real server HTML with React's own nesting rules and reports it as HP3001, with the line in the server HTML.
Use both
They overlap on the common cases, like a <div> written directly inside a <p>, which then gets two reports. Each also catches something the other does not, so running both is reasonable:
import hydrationProof from "eslint-plugin-hydration-proof";
import validateJsxNesting from "eslint-plugin-validate-jsx-nesting";
export default [
hydrationProof.configs.recommended,
{
plugins: { "validate-jsx-nesting": validateJsxNesting },
rules: { "validate-jsx-nesting/no-invalid-jsx-nesting": "error" },
},
];npm install -D eslint-plugin-hydration-proof eslint-plugin-validate-jsx-nestingSources
As of September 2026:
- The plugin's repository on GitHub, README and rule source (version 0.1.1)
- The package on npm: 41,962 downloads in the week of September 10–16, 2026; last published April 23, 2023
- eslint-plugin-react rules, no DOM nesting rule
no-invalid-interactive-nesting