Gatsby: Which is the best way to solve "window is not defined"?

Created on 16 Sep 2019  路  6Comments  路  Source: gatsbyjs/gatsby

So, my main layout component (LayoutDefault) use the window.innerWidth values to define whether if it is on a mobile device or not, it is really simple.

[...]

  const isMobile = window.innerWidth <= 500;

[...]

  { isMobile ? <LoadComponent /> : null }

Now when in building phase Gatsby throw me the classic error:

"window" is not available during server side rendering.

So I found two solutions to this problem. I'm just wonder which is best (either from the performance point of view and for the sake of complexity of the code).

The first solution is to insert an "if" inside the LayoutDefault.js file:

if (typeof window === 'undefined') {
    global.window = {}
}

It is like a defaultProps value to the global.window prop (I tried to solve using defaultProps but I couldn't)

The second solution was to use a ternary operator before loading my LayoutDefault.js, so on the index file of my gatsby site (I found this approach inside the gastsby docs):

return ( 
  typeof window !== `undefined` ? (
    <LayoutDefault location={location}>
      {children}
    </LayoutDefault> )
    : null 
)

What do you think? I prefer the first solution but I don't know if it's the best one.
Thanks in advance.

question or discussion

Most helpful comment

With the second workaround you don't get no benefit from server side rendering because you deliver only empty prerendered pages

All 6 comments

With the second workaround you don't get no benefit from server side rendering because you deliver only empty prerendered pages

With the second workaround to don't get no benefit from server side rendering because you deliver only empty prerendered pages

Thank you, @muescha , so the first one is better, despite the fact that with the first one I'm deliver an empty value for global window, could this mean that it will deliver the mobile version of the website, right?

Is this approach convenient or should I return to use media queries and don't do use this conditional mobile workaround?

I don't know the answer. Maybe you have to decide if you have more mobile users or desktop users. Also have in mind that Google is reading your prerendered site

Instead of using window.innerWidth to determine the layout, I recommend you to solve it by using library like emotion or styled-component, which will generate CSS files during SSR. With that, you can achieve responsive layout with your pre-rendered page.

Gatsby has plugins for those libraries and step-by-step documentation, check them out!

@malcolm-kee thanks for the answer. I already use emotion with styled-component style.
My problem remains though, since I'm dealing with 2 components that need different props according to the windows.width. I found this post that this is exactly my problem: Tackling responsive elements in React and why I鈥檝e created React Sizes

Still, I didn't understand very well the solution that he proposes for SSR.
I understand the concept but could it be possible to use it in a native way instead of installing react-sizes?


On the other hand I manage to "solve" the problem avoiding the use of window propriety. But still don't know if its more convenient. It works but I have the sensation that maybe it's not better from the performance point of view.

It consist on calculate the width of the rendered component.

In order to do so I changed the window.innerWidth with useLayoutEffect to get the width of the component once it's rendered
This means, by the way that I need to use: useRef, useLayoutEffect and useState from React.

const targetRef = useRef();
const [dimensions, setDimensions] = useState({ width: 0 });
useLayoutEffect(() => { 
    targetRef.current && setDimensions({ 
        width: targetRef.current.offsetWidth 
    })
}, []);
const isMobile = dimensions.width <= 500;

and then I choose the main component of the page and set a that targetRef propriety:


<Spring ect ect ref={targetRef} />

as I said this approach works but I need to use it 3 or 4 times over the web because I have more that one component that need this different props according to the width of the window. And every time I'm assigning a ref propriety to the main element of the component.

I found a new approach and it's the one I'm adopting in this case.

Basically it's like the second approach I talk about on the first post, but applied in detail.
I don't preclude the loading process of the component (since it's important to have an accurate version of the website that match the app that runs on the client and the server side rendered version), instead whenever I need an alternative prop values for an SSR version I added with a ternary operator.

In order to achieve that I just need a variable to help the building process deciding how to behave when serve the ssr version or not (we can achieve that using window object; if the window is 'undefined' then you know for sure its the ssr version):

const isServer = typeof window === 'undefined';

With this you can create ternary operators that let you use the window object without errors, because you tell react to use it only if is not SSR version.

So eventually you can dedice whether is mobile or not with window.innerWidth property. Example:

<Spring
    from={{ height: '0vh'}}
    to={{ 
     height: isServer ? '0vh' : window.innerWidth <= 500 ? '0vh' : '36vh' }}
> 
[...] 
</Spring>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Oppenheimer1 picture Oppenheimer1  路  3Comments

hobochild picture hobochild  路  3Comments

kalinchernev picture kalinchernev  路  3Comments

theduke picture theduke  路  3Comments

brandonmp picture brandonmp  路  3Comments