Using `externals` and `shims` to use React's cdn

I’m trying to use React via the CDN on a few pages and don’t think I’ve got the “shims” part down. I’ve tried to follow the Hugo ESBuild documentation, but it’s a bit confusing having an example with externals using React and the shims using Algolia. At any rate, I think I’m close, but not quite there. Can someone tell me what I’m missing?

In my template, it looks something like this:

{{- $externals := slice "react" "react-dom" -}}
{{- $opts := dict "defines" $defines "sourcemap" true "externals" $externals  -}}

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

{{ $r := "module.exports = window.React" | resources.FromString "js/shims/react.js" }}
{{ $rd := "module.exports = window.ReactDOM" | resources.FromString "js/shims/react-dom.js" }}

{{/* Call RelPermalink unnecessarily to generate JS files */}}
{{ $placebo := slice $r.RelPermalink $rd.RelPermalink }}
{{- $tsx := resources.Get "/js/health-check.tsx" | js.Build $opts | resources.Minify | fingerprint -}}

<script src="{{ $tsx.RelPermalink }}" integrity="{{ $tsx.Data.Integrity }}"></script>

in my package.json file, I have the following entry:

"browser": {
    "react": "./public/js/shims/react.js",
    "react-dom": "./public/js/shims/react-dom.js"
  },

When I go to this page in the browser, I get the following error:

Uncaught ReferenceError: require is not defined

and if I look at the generated code that causes the error, it looks like the following:

const React5 = __toModule(require("react"));

I fell into the same trap as you. After some digging I found out that you do not need to use both externals and shims, but the shim is enough. TL/DR: Just remove the "externals" in your snippet.

The externals option tells ESBuild to use require on something that already exists, mainly for NodeJs which supports require. This will not work for the Browser, where you can use the shim instead: The browser field in package.json points to a file that is read instead of the external library during the ESBuild, and here is where you want to use the existing window.React.

I would also recommend to just put a file with the shim content in assets instead of the resources.FromString workaround, because you do not need to include the shim file in the public directory and therefore your published path, you only need it during the build!

2 Likes