diff --git a/.scripts/build.mjs b/.scripts/build.mjs
index 7b8620db5..8b38407ed 100644
--- a/.scripts/build.mjs
+++ b/.scripts/build.mjs
@@ -16,7 +16,7 @@ console.log(chalk.blue('Building js'))
 await $`NODE_ENV=production rollup -c`;
 console.log(chalk.blue(`Compiling 'lib' js files`))
 // build files used for overrides
-await $`NODE_ENV=production babel src --out-dir lib`
+await $`NODE_ENV=production RBC_CJS_BUILD=true babel src --out-dir lib`
 console.log(chalk.blue(`Copying SASS files to 'lib'`))
 // and since we don't currently use CSS modules...
 await fs.copy('./src/sass', './lib/sass')
diff --git a/.size-snapshot.json b/.size-snapshot.json
index d179165d9..a5a04052d 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -24,26 +24,26 @@
     }
   },
   "react-big-calendar.js": {
-    "bundled": 1583365,
-    "minified": 445101,
-    "gzipped": 137464
+    "bundled": 1583440,
+    "minified": 445148,
+    "gzipped": 137477
   },
   "react-big-calendar.min.js": {
-    "bundled": 283536,
-    "minified": 281826,
-    "gzipped": 88860
+    "bundled": 283203,
+    "minified": 281754,
+    "gzipped": 88847
   },
   "react-big-calendar.esm.js": {
-    "bundled": 200119,
-    "minified": 93592,
-    "gzipped": 24251,
+    "bundled": 199942,
+    "minified": 93434,
+    "gzipped": 24203,
     "treeshaked": {
       "rollup": {
-        "code": 66279,
-        "import_statements": 1777
+        "code": 66290,
+        "import_statements": 1804
       },
       "webpack": {
-        "code": 70059
+        "code": 70070
       }
     }
   }
diff --git a/babel.config.esm.js b/babel.config.esm.js
new file mode 100644
index 000000000..f74e5a5e4
--- /dev/null
+++ b/babel.config.esm.js
@@ -0,0 +1,3 @@
+process.env.RBC_ESM_BUILD = 'true'
+
+module.exports = require('./babel.config')
diff --git a/babel.config.js b/babel.config.js
index 9bd27cf58..e3f44d4a2 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,6 +1,19 @@
 module.exports = function (api) {
   //api.cache(false)
 
+  const isCJSBuild = process.env.RBC_CJS_BUILD === 'true'
+  const isESMBuild = process.env.RBC_ESM_BUILD === 'true'
+  const optionalPlugins = []
+
+  if (isESMBuild) {
+    optionalPlugins.push([
+      'babel-plugin-transform-rename-import',
+      {
+        replacements: [{ original: 'lodash', replacement: 'lodash-es' }],
+      },
+    ])
+  }
+
   const config = {
     presets: [
       [
@@ -13,27 +26,25 @@ module.exports = function (api) {
           }),
         },
       ],
-      ['react-app', { absoluteRuntime: false }],
+      // FIXME: Passing `useESModules` to babel-preset-react-app is an
+      // undocumented feature. Should be avoided. This option is also deprecated
+      // according to
+      // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
+      [
+        'react-app',
+        {
+          useESModules: !isCJSBuild,
+          absoluteRuntime: false,
+        },
+      ],
     ],
     plugins: [
       ['@babel/plugin-transform-runtime'],
       ['transform-react-remove-prop-types', { mode: 'wrap' }],
       ['@babel/plugin-proposal-private-property-in-object', { loose: true }],
       ['@babel/plugin-proposal-private-methods', { loose: true }],
+      ...optionalPlugins,
     ],
-    env: {
-      esm: {
-        presets: ['@babel/preset-env', 'react-app'],
-        plugins: [
-          [
-            'babel-plugin-transform-rename-import',
-            {
-              replacements: [{ original: 'lodash', replacement: 'lodash-es' }],
-            },
-          ],
-        ],
-      },
-    },
   }
 
   return config
diff --git a/rollup.config.js b/rollup.config.js
index ee6413c2a..9a66edad2 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,3 +1,4 @@
+import path from 'path'
 import nodeResolve from '@rollup/plugin-node-resolve'
 import babel from '@rollup/plugin-babel'
 import commonjs from '@rollup/plugin-commonjs'
@@ -83,6 +84,12 @@ export default [
     },
     // prevent bundling all dependencies
     external: (id) => !id.startsWith('.') && !id.startsWith('/'),
-    plugins: [babel(babelOptions), sizeSnapshot()],
+    plugins: [
+      babel({
+        ...babelOptions,
+        configFile: path.join(__dirname, 'babel.config.esm.js'),
+      }),
+      sizeSnapshot(),
+    ],
   },
 ]
diff --git a/yarn.lock b/yarn.lock
index 5375aa943..fe957a51d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5296,9 +5296,9 @@ camelcase@^6.2.0:
   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
 
 caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001297, caniuse-lite@^1.0.30001313:
-  version "1.0.30001314"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz#65c7f9fb7e4594fca0a333bec1d8939662377596"
-  integrity sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw==
+  version "1.0.30001319"
+  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz"
+  integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw==
 
 capture-exit@^2.0.0:
   version "2.0.0"