The above component is a client component that does not trigger the hydration errors - because it never ran on the server! Did you notice how it "popped in"? Reload to see it again - remember, the red outline highlights elements that are dynamically inserted into the DOM. That's because there was no initial html rendered and delivered to the browser. After the page loaded, the Client Component loaded and rendered itself.
The source doesn't use the state tricks in the previous example to ensure that the SSR output matches the CSR output:
ClientComponent.js'use client' export default () => <p className={"client-component"}> This content is generated client-side. Datestamp: {Date.now()} </p>
The difference here is that we are importing the component in a different way in our RSC code. We've created a wrapper component around the Client Component that imports it using next/dynamic:
ClientComponentSSRWrapper.jsimport dynamic from 'next/dynamic' const ClientComponent = dynamic(() => import('./ClientComponent'), { ssr: false }) export const ClientComponentNoSSR = ()=><ClientComponent/>
So if the component doesn't SSR, what is put in its place in the generated static html?
If you View Source of this page, you'll see the static html that gets generated in place of where the Client Component will evenually be rendered:
<!--$!--><template data-dgst="DYNAMIC_SERVER_USAGE" data-msg="DYNAMIC_SERVER_USAGE" data-stck=" at ServerComponentWrapper (C:\workspace\demystifying-rsc\node_modules\next\dist\server\app-render\create-server-components-renderer.js:78:31) at InsertedHTML (C:\workspace\demystifying-rsc\node_modules\next\dist\server\app-render\app-render.js:835:33)"></template><!--/$-->
When React reconciles the static html above with the Virtual DOM - which contains a reference to the Client Component - it doesn't generate a hydration error because the above content is a valid placeholder and it knows that a Client Component will go there.
When the Client Component generates its html, that html entirely replaces the content above in the document.
The Virtual DOM representation of this page has more content and a few new things, like $Sreact.suspense:
What's going on here?
Let's dig in a little more...