4

I'm running NextJS v11.1.12, and am trying to run a standard Vanilla JS function that toggles the CSS class of the div with the class "state-select-dropdown-box". My code is:

const Header = () => {

const dropDownMenu = document.querySelector('state-select-dropdown-box');
console.log(dropDownMenu);

function toggleMenu() {
    dropDownMenu.classList.toggle('show');
}

return ( <Component /> );
}

When the app compiles, I get "Referrence Error: document is not defined". What's weird is I have got these functions running earlier today with no issues whatsoever and not changing a thing.

After some mass Googling and research, I've come to the conclusion that maybe I'm just not understanding how SSR works in NextJS? Could anyone explain or shine some light on why I'm not achieving the results that I'm expecting and how this can be solved?

S Hawley
  • 49
  • 1
  • 4

2 Answers2

10

You are getting this error because document object is not available on the server. Similarly, if you try to use window, localStorage then also it will throw the error. These are only available on client-side(browser).

NextJs serves your pages from the server. So in your case it is trying to get document on the server, but document object is not available on the server. Hence you are getting the error.

To get rid of this error:

You need to specify that such code should be run on browser side rather than on the server.

So for this, you can use typeof document

So your code should be like this:

const dropDownMenu = typeof document !== 'undefined' && document.querySelector('state-select-dropdown-box');

Ref: https://github.com/vercel/next.js/issues/5354#issuecomment-520305040

Surjeet Bhadauriya
  • 6,755
  • 3
  • 34
  • 52
2

@Surjeet Bhadauriya answer is technically correct, however next.js provide built in functionality to dynamically load components, in fact with next/dynamic you can load a component withoutSSR.
From the docs :

You may not always want to include a module on server-side. For example, when the module includes a library that only works in the browser.

const Header = () => {

const dropDownMenu = document.querySelector('state-select-dropdown-box');
console.log(dropDownMenu);

function toggleMenu() {
    dropDownMenu.classList.toggle('show');
}

return ( <Component /> );
}
export default Header

Then in your page (or wherever you import it) :

import dynamic from 'next/dynamic'

const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/Header'),
  { ssr: false } // <-- not including this component on server-side
)
Nico
  • 6,259
  • 4
  • 24
  • 40