We implemented the functionality
to turn "Dark Mode" on or off.
And this happens on the client-side of course.
There's some JavaScript code
that's executed in the browser
whenever we click this button.
But we also know that
our Next.js pages are pre-rendered
on the server side.
And in fact, if we look at the server logs
we can see that, to render the AboutPage,
our "ThemeSwitch" component function
also ran on the server.
This shouldn't come as a surprise,
but it's the first time we use
some state in one of our components,
that is the "darkMode" variable.
So it may be worth noting how
the "useState" hook
also gets called on the server.
And when that happens,
"useState" always returns the initial value,
that is "false" for "darkMode".
Now, another thing we've seen before
is that when our app runs
in development mode,
every time we reload the page
that page component is re-rendered on the server,
including any other React components it uses,
like the ThemeSwitch.
But this doesn't happen
when we run our app in production.
Let's do an "npm run build",
and generate the pages for production.
If we look at the build log,
when it generates the static pages,
you can see that
the "ThemeSwitch" component function
was called during the build.
And in fact it was executed multiple times,
because that component is used in the NavBar,
that's at the top of every page.
Other than this, there isn't much new
happening in the build,
compared to what we saw the last time.
Now, let's go and look
inside the build folder.
If we open the About page for example,
just because it's the one
we had open in the browser,
as usual the page component
has been pre-rendered to HTML,
and this includes the "Dark Mode" button.
Again, this is consistent
with what we knew from previous videos.
Now let's go and start the app
in production mode,
with "npm start".
If we load the page now,
you can see how
nothing new gets logged on the server.
Let me clear the terminal,
and we can request the About page
as many times as we like,
but the server doesn't re-render it.
It simply returns
the pre-generated HTML file
we just looked at in the build folder.
Now, let's have a look
at the Network requests.
We know that, when we load the page
the browser fetches the "about" HTML document,
that contains the pre-rendered HTML.
So the browser displays this HTML,
and at this point our ThemeSwitch
is just a plain button.
It doesn't have any event handlers attached,
so clicking the button
wouldn't actually do anything.
But, after loading the HTML document,
the browser also loads
all these JavaScript files.
These files basically contain
the React framework
along with our application code.
Next.js splits the code
into multiple "js" files
so the browser loads them more quickly,
and we don't need to worry about
what exactly is in each file,
but just as an example,
the code for our ThemeSwitch component
will be in this "_app.js" file.
So the browser executes
the JavaScript code in these files.
And it's at this point that
these messages are printed
to the browser console.
But why are the components
rendered by the browser
if they were already rendered by the server?
We touched on this before,
but I didn't really fully explain it.
We've seen that,
after loading the initial HTML document
our ThemeSwitch component is
just a basic button with no functionality.
Now, when the JavaScript code is executed,
our React app is initialised
by calling the "hydrate" function.
In a normal React app,
you typically call "ReactDOM.render()"
to insert your app into
an empty "div" element in the page.
But with Next.js the page was
already pre-rendered on the server,
and the document already contains some elements
like the "button" for our ThemeSwitch component.
So we want to somehow attach
our React components to the existing elements,
making them interactive.
This process is called "hydration".
When "hydrating" our app,
React will call our
ThemeSwitch component function,
among other components used in our app.
It's at this point that
this console message is logged.
And when our ThemeSwitch component is hydrated,
it's connected to the existing
button element in the DOM.
And it adds interactive functionality,
for example it attaches
an "onclick" event handler to the button.
Thanks to this last step
we can now click this button
and turn "Dark Mode" on or off.
It's important to understand
this two step process:
first the static HTML is rendered,
and only in a separate step, called "hydration",
the client-side functionality
of our React components
is attached to the existing document.
We can prove this by making our usual experiment
and turn JavaScript off temporarily.
Let me show the Network requests again.
If we reload the page now,
you can see that all the JavaScript files
have been blocked,
so they haven't been loaded.
And our page still looks the same initially,
because we see the static HTML,
but obviously no JavaScript code
will be executed.
As a result our "Dark Mode" button
doesn't actually work.
I can try clicking on it,
but nothing happens.
So that's why we need the "hydration" step,
to enable any interactive functionality
that we have in our page.
Pre-rendering HTML on the server is great
for the initial read-only content,
but to provide any sort of user interaction
we still need JavaScript code
running in the browser.
And that's why all our React components
are rendered twice:
first on the server,
and then again on the client side.