emotion: @emotion/cache browser specific module breaks SSR

  • emotion version: 10.0.7
  • react version: 16.8.0

Relevant code: https://github.com/emotion-js/emotion/blob/master/packages/cache/src/index.js#L69

What you did: Tried to SSR using static-site-generator-webpack-plugin.

What happened:

ERROR in ReferenceError: document is not defined at createCache
(webpack:////node_modules/@emotion/cache/dist/cache.browser.esm.js?:109:38)

Problem description: When @emotion/cache gets imported by webpack with target: web it fails due to the browser specific module not including the check for typeof document !== 'undefined'. As mentioned in https://github.com/emotion-js/emotion/issues/1113 this is a known issue and setting target: node is not an adequate solution.

Suggested solution: Drop the browser specific version of the module.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 23 (9 by maintainers)

Most upvoted comments

Thank you for this thread!

This was actually a big problem for us! We wanted to use Emotion 10 together with ReactJS.NET for using React components inside Razor pages in ASP.NET. The problem here is we can’t use target “node” because the V8 engine it is running can’t understand it (no support for require etc). So we need to use target: "web" but still enable SSR.

By manually deleting all the browser: settings from each emotion package.json like @rdadoune suggested we got it working!

So there are definately cases where a check should be made even if it is the browser specific module.

Follow up findings:

I could also make it work without the monkey patch by setting (in webpack.config.js):

 resolve: {
      aliasFields: ["module"]
},

As mentioned in #1113 this is a known issue and setting target: node is not an adequate solution.

Isnt it? Could you prepare a simple repository reproducing the issue with your setup? I could take a look then.

@GU5TAF it’s the problem of your setup. I’m not sure how static-site-generator-webpack-plugin should be used, so I can’t quite tell you how you should fix it.

The problem (the require call) is coming from react-dom/server which you try to ship to the browser by including it in your entry point with import statement.

You can check out that this is not emotion-related by removing all emotion stuff from your demo - it still won’t work.

I had the same problem, I wrote a quick monkey patch to get my build working. This script can be used post install or pre webpack. It basically unsets the “browser” mapping in the package.json of every @emotion package.

const fs = require('fs');
const { sync } = require('glob');

sync('./node_modules/@emotion/*/package.json').forEach(src => {
  const package = JSON.parse(fs.readFileSync(src, 'utf-8'));
  const browser = package.browser;
  delete package.browser;
  if (browser) {
    package._browser = browser;
  }
  fs.writeFileSync(src, JSON.stringify(package, null, 2))
});

I am having a similar issue with @emotion/react@11.1.2. I am working on a component library created using create-react-library, and using @emotion/react for styling. It works fine in many contexts, but when we try to use components from this library in a Next.js app that uses SSR, we get a similar error to the one mentioned above:

ReferenceError: window is not defined
    at Object.<anonymous> (.../node_modules/@emotion/react/dist/emotion-react.browser.esm.js:310:37)

which I believe has a similar cause, since our compiled component library has the following checks throughout (from Emotion):

var isBrowser = "object" !== 'undefined';

which leads the code down paths where things like window and document are expected to exist, which they won’t in the SSR case. I am able to work around this issue by importing the components in a try/catch, or by using Next.js’s dynamic imports, but it would be great if there was a solution that did not require workarounds. I am not sure if the issue in our case is related to some configuration (to microbundle, webpack, babel, typescript, etc.), or whether it is a genuine bug. Unfortunately, because of the nature of the project, I don’t have access to most of those configs, and since it is a private repository, I don’t have an example repo on hand to share, so I apologize for that. My understanding is that Emotion v11 with Next.js SSR “just works”, but perhaps that is only if I am using Emotion directly in a Next.js app, as opposed to my case where I am using Emotion in a standalone component library, and then installing that library as a dependency of a Next.js app. Do you have any suggestions as to how to move forward?

@rdadoune that sounds like a challenging environment to develop for 😅thanks for the monkey patch!

@Andarist I agree that it would be even more optimal for the resultant bundle to run webpack twice, once to generate the html, storing only html files and then once more to create the browser bundle.

However, this approach with a bundle that can run in both contexts isn’t that uncommon and all it would take to support it completely is for emotion not to over optimize the browser bundle.

The monkey patch @rdadoune provided us with fixes the issue, once run the current broken example in master just works. Surely this is desirable behaviour?

I’ve applied it as a pre build step in a new branch called rdadoune if you’re curious.

The problem with the environment I was using is that it’s not for the browser or for node, the script needs to be run ad-hoc via Google V8 JS engine.

The root cause of this is an optimisation done in the module bundler preconstruct that replaces "typeof document" with "object" and is then removed by a minifier that does dead code removal. This is definitely the intended behaviour but perhaps requires some rethinking as its intention probably wasn’t to make it impossible to use static-site-generator-webpack-plugin amongst others.

so how to fix it? Now in mui 5 with next i got this error

@breno-sapucaia sorry, I wish I would have included an example of the workaround at the time, because I don’t recall exactly what we did now, and I no longer have access to that code base. This example shows one thing we tried that worked, but we didn’t like having to use that work around. Ultimately, we ended up going moving to styled-components for various reasons that I don’t recall. Wish I could be of more help, but I’m afraid I never really came to a good understanding of the problem or a good solution.