March 2026posted on 03.29.2026HTML Button Nesting Causes Hydration Errors In HTML, <button> cannot be a descendant of another <button>. Browsers silently restructure the DOM when they encounter this, but React's server-rendered HTML does not match the browser's restructured DOM, causing a hydration mismatch. // BAD: nested button causes hydration error <button onClick={openMenu}> <Icon /> {isOpen && ( <div className="submenu"> <button onClick={selectOption}>Option A</button> {/* nested! */} </div> )} </button> // GOOD: move submenu outside the button, wrap both in a div <div onMouseEnter={showMenu} onMouseLeave={hideMenu}> <button onClick={openMenu}> <Icon /> </button> {isOpen && ( <div className="submenu"> <button onClick={selectOption}>Option A</button> </div> )} </div> The error message in Next.js dev mode is: In HTML, <button> cannot be a descendant of <button>. This will cause a hydration error. This also applies to <a> inside <a>, <form> inside <form>, and other elements with restrictions defined in the HTML spec's content model.No reactions yet
HTML Button Nesting Causes Hydration Errors
In HTML,
<button>cannot be a descendant of another<button>. Browsers silently restructure the DOM when they encounter this, but React's server-rendered HTML does not match the browser's restructured DOM, causing a hydration mismatch.The error message in Next.js dev mode is:
In HTML, <button> cannot be a descendant of <button>. This will cause a hydration error.This also applies to
<a>inside<a>,<form>inside<form>, and other elements with restrictions defined in the HTML spec's content model.