4

I used to render a component depending on the screen width by writing something like this.

function App(props) {
    const [mobile, setMobile] = useState(() => window.innerWidth < 576 ? true : false)
    return (
        <div>
            {
                mobile ? <ComponentA /> : <ComponentB />
            }
        </div >
    );
}

But now that I'm using Next.js this gives me several errors due to the window.innerWidth reference. How could I do this? Thanks in advance.

4
  • Welcome to S.O. What's the error msg ? Commented Feb 13, 2021 at 5:39
  • Might check out medium.com/frontend-digest/…. Commented Feb 13, 2021 at 5:40
  • NextJS is isomorphic (runs on server & client). window is undefined on the server so you'll see an error. You need to do a check like window && window.innerWidth < 576 Commented Feb 13, 2021 at 6:04
  • window && will still throw server-side. typeof can be used to check whether variables have been declared safely, e.g. typeof window !== 'undefined' && window.innerWidth < 576 ? true : false Commented Feb 13, 2021 at 6:16

2 Answers 2

3

You are getting a reference error because you cannot access the window object in useState. Instead, you have to set the initial value in useState to undefined or null and use useEffect where window can be referenced to call setMobile(window.innerWidth < 576 ? true : false). finally, in your render method, you can check whether mobile state is set using setMobile (i.e., not undefined or null) and use the defined mobile state value (either true or false) to conditionally render your ComponentA or ComponentB. Also, you need to add window.addEventListener('resize', handleResize) when your App component is mounted and remove it when it is unmounted, which you also do in useEffect since that is where you get reference to window. Otherwise, resizing the browser will not trigger an update to mobile state. Here is a working example:


import React, { useState, useEffect } from 'react'

function App() {
  const [mobile, setMobile] = useState(undefined)

  useEffect(() => {
    const updateMobile = () => {
      setMobile(window.innerWidth < 576 ? true : false)
    }

    updateMobile()
    window.addEventListener('resize', updateMobile)
    return () => {
      window.removeEventListener('resize', updateMobile)
    }
  }, [])

  return typeof mobile !== 'undefined' ? (
    mobile ? (
      <ComponentA />
    ) : (
      <ComponentB />
    )
  ) : null
}
Sign up to request clarification or add additional context in comments.

1 Comment

Wow! that was awesome. It works fine. I was all day long trying with the useState, useEffect and process.browser, reading about the not window in the server side. I was getting infinite loop errors for either one or another component in the first render. The first render doesn't show the componentA nor the componentB. That was the key. Thansk very much.
0

Assuming you're seeing something along the lines of ReferenceError: window is not defined:

ReferenceError is thrown when a non-existent variable is referenced.

This is occurring because, in NextJS, components are often initially rendered server-side, using NodeJS, before being handed over for clients to consume. Additionally, in NodeJS, there is no such thing as window — hence, window is not defined.

Fortunately, typeof can be used in such cases to safely check variables before attempting to use them (see this SO answer for additional info).

See below for a practical example.

const [mobile, setMobile] = useState(() => {
  if (typeof window === 'undefined') return false
  return window.innerWidth < 576
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.