If components require interactivity or hooks like useState, or are basically any "typical" React component, then they expect to run in the browser, not on the server.
The default with RSC is to treat components as server components, and you must opt into treating them as client components that run in the browser.
This differs with the previous /pages directory in NextJS, for example, where everything is a client component and you opted into server-side behavior by defining a getServerProps() method etc to control SSR.
'use client' export const ClientComponent = ()=>{ console.log('Running client component'); return <div className={"client-component"}>Client Component HTML</div> }
This component was delivered as javascript to the browser where it ran, generated html, and then that html was inserted into the DOM.
But if you View Source you will see that static html was returned from this client component. Why would that happen if it's a client component?
Because by default, client components are pre-rendered on the server, and their html is included in the static output. This is called SSR and has been around for a while. It's different than RSC.
If you look in your browser console, you will see a log statement showing that the client component DID run.
"Running client component"
So let's dissect what just happened:
The relevant pieces of the React Virtual DOM for the client component are below.
The 1st line is a reference to the JS file that contains the component.
The 2nd line is the entire Virtual DOM.
Below we have chopped down the long Virtual DOM line to show the important part:
The highlighted part of the second row is a reference of the form $L[id] where the Client Component should be in the DOM (right after the header, see above in the page). This tells React to resolve the [id] line and if/when it exists, execute the component and insert its output in this position in the DOM.
The reason that this component was split into a separate js file and inserted as a reference in the Virtual DOM is because this is a Client Component.
The 'use client'
directive at the top of the Component's source file is what told the bundler and RSC to treat it that way.
More on that later.
As React reconciles the DOM created from the static html with its Virtual DOM, it recognizes that part of the static DOM was SSR output from a Client Component. It knows this because the $L[id] reference in the Virtual DOM is in place of the html in the static DOM.
At this point, React renders the Client Component. The resulting Client Component html is inserted into the DOM where the static html is currently.
This is called Hydration - turning static SSR html into dynamic client-side (CSR) html.
At this point, React also compares the SSR html to the CSR html, and expects them to be identical. This is because it assumes the initial state of the component as rendered on the server should match the initial state of the component as rendered in the browser.
But they aren't always the same...
Hydration Failure