[bug]: Initially opened dialog causes hydration error
isRyven opened this issue · 1 comments
Describe the bug
Using a Dialog component, which is initially opened, throws a hydration exception.
Affected component/components
Dialog
How to reproduce
1. Initiate latest next project:
npx create-next-app@latest dialog-bug-shadcn --typescript --tailwind --eslint
cd dialog-bug-shadcn
2. Initiate shadcn:
npx shadcn-ui@latest init
3. Add Dialog component:
npx shadcn-ui@latest add dialog
4. Use the Dialog
component with open
property set to true
:
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
export default function Home() {
return (
<div className="p-4">
<Dialog open={true} modal={false}>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
);
}
5. Run the next dev server:
npm run dev
Codesandbox/StackBlitz link
https://stackblitz.com/edit/stackblitz-starters-c11bus
Logs
Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.
See more info here: https://nextjs.org/docs/messages/react-hydration-error
Expected server HTML to contain a matching <div>
in <div>
.
<Home>
<div>
^^^^^
<$5d3850c4d0b4e6c7$export$3ddf2d174ce01153>
<Provider>
<_c1>
<$5d3850c4d0b4e6c7$export$dad7c95542bacce0>
<Provider>
<$921a889cee6df7e8$export$99c2b779aa4e8b8b>
<eval>
<eval>
<eval>
<eval>
<eval>
<$921a889cee6df7e8$export$99c2b779aa4e8b8b>
<eval>
<eval>
<eval>
<eval>
<eval>
<eval>
<eval>
<eval>
<div>
Callstack
throwOnHydrationMismatch
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (6981:1)
tryToClaimNextHydratableInstance
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (7016:1)
updateHostComponent$1
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (16621:1)
beginWork$1
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (18503:1)
HTMLUnknownElement.callCallback
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (20565:1)
Object.invokeGuardedCallbackImpl
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (20614:1)
invokeGuardedCallback
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (20689:1)
beginWork
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (26949:1)
performUnitOfWork
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25748:1)
workLoopConcurrent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25734:1)
renderRootConcurrent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25690:1)
performConcurrentWorkOnRoot
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (24504:1)
workLoop
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (256:1)
flushWork
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (225:1)
MessagePort.performWorkUntilDeadline
node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (534:1)
System Info
Sytem:
OS: macOS 14.4.1 23E224 arm64
Host: MacBookPro18,3
Kernel: 23.4.0
CPU: Apple M1 Pro
GPU: Apple M1 Pro
Browser:
Arc
Version 1.40.0 (49176)
Chromium Engine Version 124.0.6367.79
Before submitting
- I've made research efforts and searched the documentation
- I've searched for existing issues
Hi @isRyven
According to the official Next.js documentation on hydration errors, a hydration error occurs when:
While rendering your application, there was a difference between the React tree that was pre-rendered from the server and the React tree that was rendered during the first render in the browser.
Since the dialog initially requires JavaScript to render, a discrepancy can arise. After the server renders the HTML, the client-side JavaScript executes and potentially introduces new HTML content, which may not match the HTML that was rendered by the server.
To solve this, you have two options:
- Import the Dialog component dynamically on the client side and disable SSR (Server-Side Rendering) for it:
const Dialog = dynamic(() => import('@/components/ui/dialog').then((mod) => mod.Dialog), { ssr: false });
- Delay rendering until after the server has finished generating the HTML. This can be managed by checking a state that is set only on the client:
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, [])
When isClient is true, it indicates that the client has taken over rendering from the server.
Cheers!