Next.js: Example of using babel-polyfill

Created on 25 May 2017  路  16Comments  路  Source: vercel/next.js

After installing babel-polyfill, in next.config.js

module.exports = {
  webpack: (config, { dev }) => {
    var oldEntry = config.entry;
    config.entry = function () {
      return oldEntry().then(function (entries) {
        entries['main.js'].unshift('babel-polyfill');
        return entries;
      });
    }

    return config;
  }
};

Most helpful comment

Things have come a long way. babel-polyfill now plays well with babel-preset-env so you can include only the polyfills for your target environments/browsers. To do that:

// next.config.js
module.exports = {
  webpack: (config) => {
    // Unshift polyfills in main entrypoint.
    const originalEntry = config.entry;
    config.entry = async () => {
      const entries = await originalEntry();
      if (entries['main.js']) {
        entries['main.js'].unshift('./polyfills.js');
      }
      return entries;
    };

    return config;
  }
}
// polyfills.js
import 'babel-polyfill';

Babel 6

// .babelrc
{
  "presets": [
    [
      "next/babel",
      {
        "preset-env": {
          "targets": {
            "browsers": "defaults"
          },
          "useBuiltIns": true
        }
      }
    ]
  ]
}

Babel 7

// .babelrc (Babel 7)
{
  "presets": [
    [
      "next/babel",
      {
        "preset-env": {
          "useBuiltIns": "usage"
        }
      }
    ]
  ]
}
// .browserslistrc
defaults

You still need a separate polyfills.js file with the import statement, as simply unshifting babel-polyfill as the original post will include all the polyfills.

Would be good to consider this as default in Next though.

All 16 comments

We use this automatically as you use features which needs a polyfill. You don't need to use it manually.

Well, the version I was on v2.4.0 doesn't work (on IE 11, etc) if I didn't include the script I wrote above. I got errors for null calling on "includes" methods etc.
I assume that means polyfill either isn't included automatically or what it was there didn't work at all.

@pencilcheck I assume this is related to a module in your app. It doesn't come with a polyfill. In that case you need to use it.
But including the whole babel-polyfill is not a good option. It's huge.

May be you can add the exact polyfill you need. Use core-js
That's what's inside babel-polyfill

Things have come a long way. babel-polyfill now plays well with babel-preset-env so you can include only the polyfills for your target environments/browsers. To do that:

// next.config.js
module.exports = {
  webpack: (config) => {
    // Unshift polyfills in main entrypoint.
    const originalEntry = config.entry;
    config.entry = async () => {
      const entries = await originalEntry();
      if (entries['main.js']) {
        entries['main.js'].unshift('./polyfills.js');
      }
      return entries;
    };

    return config;
  }
}
// polyfills.js
import 'babel-polyfill';

Babel 6

// .babelrc
{
  "presets": [
    [
      "next/babel",
      {
        "preset-env": {
          "targets": {
            "browsers": "defaults"
          },
          "useBuiltIns": true
        }
      }
    ]
  ]
}

Babel 7

// .babelrc (Babel 7)
{
  "presets": [
    [
      "next/babel",
      {
        "preset-env": {
          "useBuiltIns": "usage"
        }
      }
    ]
  ]
}
// .browserslistrc
defaults

You still need a separate polyfills.js file with the import statement, as simply unshifting babel-polyfill as the original post will include all the polyfills.

Would be good to consider this as default in Next though.

I just want to leave a note here that after upgrading to Next 6, after every navigation that required a server build, HMR informed me that "The following modules couldn't be hot updated: (Full reload needed)" because - undefined module could not be hot replaced.

screen shot 2018-05-04 at 11 29 16 pm

I noticed that multiple babel-polyfill's were being added to the bundle (by console.log the moduleMap in webpack-hot-middleware/client.js) each time a page needed to be built.

I removed the babel-polyfill code and the issue went away. So maybe something to be cautious of.

@joaovieira a couple of comments on that:

useBuiltins does not appear to accept a value of true now, see the error below:

Module build failed: Invariant Violation: [BABEL] ~/pages/_error.js: Invalid Option: The 'useBuiltIns' option must be either
    'false' (default) to indicate no polyfill,
    '"entry"' to indicate replacing the entry polyfill, or
    '"usage"' to import only used polyfills per file (While processing: "~/node_modules/next/babel.js$0")
    at invariant (~/node_modules/invariant/invariant.js:40:15)
    at validateUseBuiltInsOption (~/node_modules/next/node_modules/@babel/preset-env/lib/normalize-options.js:130:26)

also there seems to be no need to hack the webpack entries, you can use the clientBootstrap in next.config.js:

exports = module.exports = {
   // ...
   clientBootstrap: [path.join(__dirname, './app/polyfills.js')]
   // ...
};

with the same content in polyfills.js as in your example.

clientBootstrap is not part of the Next.js codebase at this point.

To add IE11 Array.includes support I added includes as polyfill from core-js. But now I have the same issue as @vjpr.

screen shot 2018-05-14 at 14 12 45

If I just removed the polyfill part from the config it works as expected and doesn't reloads at every page navigation.

I don't get it yet how to fix it. Following some code example:

next.config.js

module.exports = {
    webpack: function(cfg) {
        const originalEntry = cfg.entry;

        cfg.entry = async() => {
            const entries = await originalEntry();

            if(entries['main.js']) {
                entries['main.js'].unshift('./src/polyfills.js');
            }

            return entries;
        };

        return cfg;
    },
};

src/polyfill,js

/* eslint no-extend-native: 0 */
import includes from 'core-js/library/fn/array/virtual/includes';

Array.prototype.includes = includes;

Facing the same issue, for now I conditionally only add my polyfills for production

    webpack: function(cfg, { dev }) {
        if (!dev) {
            const originalEntry = cfg.entry;
            cfg.entry = async() => {
            // ...

Thanks to @joaovieira for your example. It solved my issue for supporting IE11. If you are on Babel 7 (Next 6.x and beyond), a small change in the preset is required. Here is the updated code (including an example of transpiling a node module that used an arrow function:

This assumes you have a file called polyfills.js that just has a import @babel/polyfill.

const path = require('path')

module.exports = {
  webpack: config => {
    // Unshift polyfills in main entrypoint.
    const originalEntry = config.entry
    config.entry = async () => {
      const entries = await originalEntry()
      if (entries['main.js']) {
        entries['main.js'].unshift('./polyfills.js')
      }
      return entries
    }
    // This lib has an arrow fn causing IE11 to explode
    config.module.rules.push({
      test: path.resolve('./node_modules/superagent-charset/index.js'),
      loader: 'babel-loader',
      options: {
        babelrc: false,
        cacheDirectory: true,
        presets: ['@babel/preset-env'],
      },
    })

    return config
  },
}

Are the imports in polyfills.js being transpiled by babel?
I'm including a polyfill for Object proxy https://github.com/GoogleChrome/proxy-polyfill
and IE is throwing on the template string in one of the errors. I confirmed that preset-env is applying the babel plugin for converting template strings.

Does this solution works only for the client side code ? I keep on getting regeneratorRuntime is not defined if I use async/await on server side code.

With babel 7 and nextjs 7, just add useBuiltIns: "usage" in .babelrc and it works:

module.exports = {
  presets: [
    [
      "next/babel",
      {
        "preset-env": {
          targets: {
            browsers: [">0.03%"]
          },
          useBuiltIns: "usage"
        }
      }
    ]
  ]
};

Source : https://babeljs.io/docs/en/babel-polyfill#usage-in-node-browserify-webpack

Note that this will NOT polyfill anything that node_module dependencies depend on, since it only inserts polyfills for stuff that is babel transformed, and by default node_modules are not babel transpiled when building the client project (which is a good thing).

@joaogranado Using your solution with babel 7 gives this error (warning?) on build:

When setting `useBuiltIns: 'usage'`, polyfills are automatically imported when needed.
Please remove the `import '@babel/polyfill'` call or use `useBuiltIns: 'entry'` instead.

I changed "useBuiltIns": "usage" to "useBuiltIns": "entry" and it _seems_ to work, not sure it's the right way though...

@rap2hpoutre for whatever reason, this solution isn't working for us - still getting Object does not support property or method 'assign' errors in IE 11. Is there anything further we need beyond the configuration change in .babelrc? (the entries business, perhaps?)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

havefive picture havefive  路  3Comments

swrdfish picture swrdfish  路  3Comments

ghost picture ghost  路  3Comments

lixiaoyan picture lixiaoyan  路  3Comments

formula349 picture formula349  路  3Comments