Gatsby: build(webpack) : Window is not defined

Created on 14 Apr 2019  路  10Comments  路  Source: gatsbyjs/gatsby

Description

While using an npm lib called canvas-confetti,
everything is working awesome during development. While building the prod build using gatsby build it showed this error :point_down:

Screenshot from 2019-04-12 23-38-03

Steps to reproduce

Install canvas confetti & try to build a static html

Expected result

It should build successfully

Actual result

To get a successful build I removed the lib from local import & used a cdn version directly. Just for building.

Environment

OS : Linux
Node v10.12.0

stale? awaiting author response needs reproduction question or discussion

Most helpful comment

@orshick this is the beauty of Gatsby and the underlying technology, namely React. Depending on the use case scenario, you have at your disposal a myriad of ways to solve your issue.

For instance:

  • You can get the package via a CDN, hook it to the "global" via Helmet/React-Helmet and in your class components access it, bear in mind that you might need to apply the sanity check, namely if (typeof window!==undefined.
  • You can install react loadable and use it as a wrapper for a component that wants to use a client side only package, not tested it extensively with Gatsby or any other server side rendering technology but what i've done with it showed me promising results.
  • You can go with this route, with a ref, but bear in mind that certain packages allow es6 import as contrary to this one and that helps out a bit.

On a side note, a more personal note, me personally i tend to be biased towards JQuery, i believe that the package does a good work, but i've had my share of problems trying to make React and JQuery play nice, the biggest reason being, because they tend to "fight" over control over the DOM and that has led me to some real "hair pulling"(pardon the bad pun) situations.

@pranshuchittora care to elaborate on:

creating a separate class component for this doesn't look good & might eat a lot of my bundle budget.

Depending on what you're going to do it, it's not one class that will increase the bundle that significantly. Also how you do it. Gatsby with the underlying packager webpack does minify, if not mistaken applies tree shaking and so on. The build will be optimized for production.

Also the code i've shown can be improved and you can call the package when you need it.

All 10 comments

@pranshuchittora as you may or may not know, Gatsby in development mode is way more permissive and let's you get a way with certain things that you won't when you issue a production build. When you issue a production build almost if not all of the work is done through the server side, more specifically through node and it does not have access to certain apis, like for instance this one, window and that's the reason you'll get that error. Bear in mind that most developers when working on a package, most of the time don't take in consideration Gatsby and other server side rendering generators. They push content based on the fact that you'll use it in the client either with CRA(Create React APP), or your own project setup and leave you to handle it. Also the README clearly states that:

You can then require('canvas-confetti'); to use it in your project build. Note: this is a client component, and will not run in Node. You will need to build your project with something like webpack in order to use this.

The operative word here being client.

Alternatively you can use a ref with a full blown class component like this:

import React, { Component } from "react"

class ConfettiWithRef extends Component {
  constructor(props) {
    super(props)
    this.confettiCanvas = React.createRef()
  }
  componentDidMount() {
    if (typeof window !== "undefined") {
      const confetti = require("canvas-confetti")// es 6 import will not work, will throw error window not defined
      const confettis = document.createElement("canvas")
      this.confettiCanvas.current.appendChild(confettis)
      const myConfetti = confetti.create(confettis, { resize: true })
      myConfetti({ particleCount: 100, spread: 160 })
    }
  }


  render() {
    return (
      <div>
        <h1>confetti with a ref</h1>
        <div ref={this.confettiCanvas} />
      </div>
    )
  }
}

export default ConfettiWithRef

````

Add it to a page like so:
```javascript
import React from "react"
import ConfettiWithRef from "../components/ConfettiWithRef"
export default () => (
  <div>
    <ConfettiWithRef />
  </div>
)

Issue gatsby build && gatsby serve open http://localhost:9000 and you'll have the confettis shown. Transforming this into a "controlled" approach should be a trivial matter.

@jonniebigodes Sorry, I'm not very familiar with Webpack, but is there not some way to opt out of server side rendering certain libraries? Since this library is a client side one, presumably it wouldn't benefit from server rendering anyway, so it would be great if there was simply a way for gatsby/webpack to not bother with that.

I recall following some instructions to achieve an effect like this in gatsby v1 to use jquery in a site I was converting from wordpress to gatsby, but the webpack config api has changed since then in a way that it wasn't obvious to me how to port the same code to v2.

Sadly in react world you'll need do some extra work like @jonniebigodes described.

if you can create a reproduction it might help us fix your issue.

@jonniebigodes creating a separate class component for this doesn't look good & might eat a lot of my bundle budget.

Are there any GATSBY global variables, which I state the current env.
for example
(While developing)
$GATSBY_ENV = 'development'
(While building)
$GATSBY_ENV = 'production'

@orshick this is the beauty of Gatsby and the underlying technology, namely React. Depending on the use case scenario, you have at your disposal a myriad of ways to solve your issue.

For instance:

  • You can get the package via a CDN, hook it to the "global" via Helmet/React-Helmet and in your class components access it, bear in mind that you might need to apply the sanity check, namely if (typeof window!==undefined.
  • You can install react loadable and use it as a wrapper for a component that wants to use a client side only package, not tested it extensively with Gatsby or any other server side rendering technology but what i've done with it showed me promising results.
  • You can go with this route, with a ref, but bear in mind that certain packages allow es6 import as contrary to this one and that helps out a bit.

On a side note, a more personal note, me personally i tend to be biased towards JQuery, i believe that the package does a good work, but i've had my share of problems trying to make React and JQuery play nice, the biggest reason being, because they tend to "fight" over control over the DOM and that has led me to some real "hair pulling"(pardon the bad pun) situations.

@pranshuchittora care to elaborate on:

creating a separate class component for this doesn't look good & might eat a lot of my bundle budget.

Depending on what you're going to do it, it's not one class that will increase the bundle that significantly. Also how you do it. Gatsby with the underlying packager webpack does minify, if not mistaken applies tree shaking and so on. The build will be optimized for production.

Also the code i've shown can be improved and you can call the package when you need it.

You can create this react component that should work;

import React, { useRef, useEffect } from "react";

const CanvasConfetti = () => {
  const canvasRef = useRef(null);

  useEffect(() => {
    import("canvas-confetti").then(confetti => {
      const myConfetti = confetti.create(canvasRef.current, { resize: true });
      myConfetti({
        particleCount: 100,
        spread: 160
        // any other options from the global
        // confetti function
      });
    });
  }, []);

  return <canvas ref={canvasRef} />;
};

export default CanvasConfetti;

you can see a demo here
https://codesandbox.io/s/xp11mq39o?fontsize=14

Hiya!

This issue has gone quiet. Spooky quiet. 馃懟

We get a lot of issues, so we currently close issues after 30 days of inactivity. It鈥檚 been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contributefor more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 馃挭馃挏

Popping in here to say thanks to @jonniebigodes for your incredibly informative comment above. react-loadable is making my dreams come true over here. Thank you!

That tip belongs in the docs somewhere - I'd be happy to put it in a PR if that's ok with you @jonniebigodes 馃憤

@mbifulco no need to thank, really glad that it worked out for you. If you're willing to make the adjustment for, it it's fine by me. Read the contribution docs, make the pull request. I hope to see it being merged 馃憤

Hey again!

It鈥檚 been 30 days since anything happened on this issue, so our friendly neighborhood robot (that鈥檚 me!) is going to close it.

Please keep in mind that I鈥檓 only a robot, so if I鈥檝e closed this issue in error, I鈥檓 HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else.

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks again for being part of the Gatsby community!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dustinhorton picture dustinhorton  路  3Comments

andykais picture andykais  路  3Comments

ghost picture ghost  路  3Comments

magicly picture magicly  路  3Comments

jimfilippou picture jimfilippou  路  3Comments