Ok, so we're trying to read a value
from "localStorage",
but we're getting an error
saying that "localStorage is not defined".
Let's make sure we fully understand
what's going on here.
When we run our JavaScript code
in the browser,
and I'll use the interactive console
to demonstrate it,
there is a global object
called "localStorage",
which happens to be an instance
of the Storage interface.
But when our code run on the server
it runs in Node.js,
and if we try accessing
"localStorage" from here,
we get an error.
Which is in fact exactly the same error
we got in our application
when trying to read the "darkMode" value.
Our "loadDarkMode" function runs
in two different JavaScript environments:
Node.js on the server,
and in the browser.
Local Storage is a web API
that's only available in the browser.
So before we try using it
we need to check if it's available.
A good way to do that
is to use the "typeof" operator.
In the browser,
"localStorage" is of type "object".
While if we try the same thing in Node.js
we can see that here "localStorage"
is "undefined".
Now, let me quit the interpreter,
and restart the dev server.
So we want to do the same check
in our "loadDarkMode" function.
If "typeof localStorage"
is "undefined"
then this effectively means
we're running on the server.
In this case we can return
a default value for the "darkMode",
that is "false".
This means the static HTML pages
generated on the server
will always use the light theme.
With this small change
the error should go away.
You can see that the page
is rendered successfully,
and it has a dark background,
so it used the saved "darkMode" value.
We can test that
everything else still works.
If we click the button
of course it will toggle the theme,
and update the value in localStorage.
So, remember that with Next.js,
whenever you use some browser-specific feature
in code that also runs in the server
you should first check
if that feature is available.
Note that in the "handleClick" function
there's no need to check
if "localStorage" is available,
because the button click event
can only occur in the browser,
so this code will never run on the server.
Anyway, we're not quite finished yet.
If we look at the browser console,
there seems to be another error.
Let me refresh the page,
to show when that happens.
It's not strictly an error,
it's actually a warning,
but it's a very interesting one.
It says "Text content did not match."
"Server: Dark Mode"
"Client: Light Mode"
Remember all our discussion
about "hydration" in the previous video?
That will help us understand
what's happening here.
Let me show the server logs,
and add a divider.
Now, we said many times that
when we load the page
first it's pre-rendered on the server,
and on the server
"darkMode" is always set to "false".
Then our React component
is re-rendered in the browser.
Or more accurately it's "hydrated".
But in the browser "darkMode" is set to "true",
because its value is initialised
from Local Storage".
So there is a discrepancy compared to
what was rendered by the server.
In the initial static HTML
the text inside the "button" was "Dark Mode",
but when the same component
is rendered in the browser,
the text says "Light Mode".
So during the hydration phase
React tries to connect
our ThemeSwitch component
to the existing DOM element
but it detects that
something is different.
That's why it prints that warning
that we see in the console.
It's basically React telling us
something unusual is going on here;
you may want to check if
there's a problem with your code.
But if we do go
and look at our code,
we'll see that actually
there is nothing wrong in this case.
The text inside this button
can indeed be different
between server and browser,
and for a good reason.
If "darkMode" was saved as "true" in localStorage
the browser should override
the initial text set by the server,
and that's perfectly fine.
So as long as we're sure
that it's not a problem
we can set a special React property
called "suppressHydrationWarning".
Which does exactly what it says:
it makes the warning go away.
But we should only use this
in special cases.
With this our example is complete.
We've seen how to implement
some client side functionality
with Next.js.
It's mostly the same as
with normal React apps,
but there are some cases
where we need to keep in mind
that our code also runs on the server,
to pre-render our static pages.