Search code examples
reactjswebpackwebpack-dev-servercreate-react-apphtml-webpack-plugin

NODE_ENV in webpack react app is not being set corretly


I'm building a simple react app that I'm deploying to S3. I used's facebook's create-react-app as a starting point. I want to add a conditional in my index.html so that I'm only including Google Analytics in production.

To test that it's working I have code like this: (I intend to change the string below to 'production' once I validate that everything is working correclty)

<%= '%NODE_ENV%' %>
<% if ('%NODE_ENV%' == 'development') { %>
  <!-- Global site tag (gtag.js) - Google Analytics -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=XXX"></script>
<% } %>

When I load my app locally using npm run start. I see that it prints development on the page but that the Google Analytics script tag is NOT on the page. How is it possible that on my first line of webpack JS %NODE_ENV% is equal to 'development' and then on the very next line that condition fails??

Here's is the HTML that is output locally:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
    development

  <script type="text/javascript" src="/static/js/bundle.js"></script></body>
</html>

As you can see it prints 'development' but then has an empty line where the script tag would have gone had the condition passed.

I'm new to webpack and I'm very confused about this behavior. Any help would be much appreciated!


Solution

  • After much investigation, I think I've figured out what's going on here. The reason the first line is correct and then the second isn't has nothing to do with your code, but with the way create-react-app actually inserts those environment variables.

    create-react-app is using a plugin call InterpolateHtmlPlugin, which calls a hook from HtmlWebpackPlugin. Basically what InterpolateHtmlPlugin does is look for %env_var_name% in the HTML and replace it with the corresponding value in the environment object in the app.

    The issue is by the time the HTML makes it to the InterpolateHtmlPlugin the template code has already been processed (after my tests, I'm pretty sure this is true). Since it's already been processed that conditional has evaluated to false and doesn't show up in the HTML anymore. For the first line, it shows up at %NODE_ENV%, so Interpolate picks it up and replaces it.

    I think the easiest thing to do is to add the NODE_ENV to your HtmlWebpackPlugin in your webpack configs and reference it that way. So your new HtmlWebpackPlugin configuration would look like this:

    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
      NODE_ENV: env.raw.NODE_ENV
    })
    

    and the conditional in your index.html file would look like this:

    <% if (htmlWebpackPlugin.options.NODE_ENV == 'development') { %>
      <!-- Global site tag (gtag.js) - Google Analytics -->
      <script async src="https://www.googletagmanager.com/gtag/js?id=XXX"></script>
    <% } %>