reactjsnext.jsserver-side-renderingreact-dates

How to detect window size in Next.js SSR using react hook?


I am building an app using Next.js and react-dates.

I have two component DateRangePicker component and DayPickerRangeController component.

I want to render DateRangePicker when the window's width is bigger than size 1180px, if the size is smaller than this I want to render DayPickerRangeController instead.

Here is the code:

      windowSize > 1180 ?
           <DateRangePicker
             startDatePlaceholderText="Start"
             startDate={startDate}
             startDateId="startDate"
             onDatesChange={handleOnDateChange}
             endDate={endDate}
             endDateId="endDate"
             focusedInput={focus}
             transitionDuration={0}
             onFocusChange={(focusedInput) => {
               if (!focusedInput) {
                 setFocus("startDate")
               } else {
                 setFocus(focusedInput)
                }
               }}
                /> :
             <DayPickerRangeController
               isOutsideRange={day => isInclusivelyBeforeDay(day, moment().add(-1, 'days'))}
               startDate={startDate}
               onDatesChange={handleOnDateChange}
               endDate={endDate}
               focusedInput={focus}
               onFocusChange={(focusedInput) => {
               if (!focusedInput) {
                 setFocus("startDate")
                 } else {
                  setFocus(focusedInput)
                 }
               }}
              /> 
          }

I normally use react hook with window object to detect window screen width like this

But I found that this way is not available when ssr because ssr rendering does not have window object.

Is there an alternative way I can get window size safely regardless of ssr?


Solution

  • You can avoid calling your detection function in ssr by adding this code:

    // make sure your function is being called in client side only
    if (typeof window !== 'undefined') {
      // detect window screen width function
    }
    

    full example from your link:

    import { useState, useEffect } from 'react';
    
    // Usage
    function App() {
      const size = useWindowSize();
    
      return (
        <div>
          {size.width}px / {size.height}px
        </div>
      );
    }
    
    // Hook
    function useWindowSize() {
      // Initialize state with undefined width/height so server and client renders match
      // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
      const [windowSize, setWindowSize] = useState({
        width: undefined,
        height: undefined,
      });
    
      useEffect(() => {
        // only execute all the code below in client side
        // Handler to call on window resize
        function handleResize() {
          // Set window width/height to state
          setWindowSize({
            width: window.innerWidth,
            height: window.innerHeight,
          });
        }
        
        // Add event listener
        window.addEventListener("resize", handleResize);
         
        // Call handler right away so state gets updated with initial window size
        handleResize();
        
        // Remove event listener on cleanup
        return () => window.removeEventListener("resize", handleResize);
      }, []); // Empty array ensures that effect is only run on mount
      return windowSize;
    }
    

    NB: Updated as Sergey Dubovik comment, we dont need to validate windows since useEffect run in client side