diff --git a/doc/api/esm.md b/doc/api/esm.md
index 459f87771815cc..f20b6516b38dd1 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -45,6 +45,10 @@ The `import.meta` metaproperty is an `Object` that contains the following
 property:
 
 * `url` {string} The absolute `file:` URL of the module.
+* `require` {Function} To require CommonJS modules. This function enables
+  interoperability between CJS and ESM. See [`require()`]. None of the
+  properties generally exposed on require are available via
+  `import.meta.require`.
 
 ### Unsupported
 
@@ -256,3 +260,4 @@ in the import tree.
 [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
 [addons]: addons.html
 [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
+[`require()`]: modules.html#modules_require
diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js
index db28ca04b16cdc..cfae050d8c37be 100644
--- a/lib/internal/process/esm_loader.js
+++ b/lib/internal/process/esm_loader.js
@@ -1,12 +1,16 @@
 'use strict';
 
+const { makeRequireFunction } = require('internal/modules/cjs/helpers');
+const Module = require('module');
 const { internalBinding } = require('internal/bootstrap/loaders');
+
 const {
   setImportModuleDynamicallyCallback,
   setInitializeImportMetaObjectCallback
 } = internalBinding('module_wrap');
+const { defineProperty } = Object;
 
-const { getURLFromFilePath } = require('internal/url');
+const { getURLFromFilePath, getPathFromURL } = require('internal/url');
 const Loader = require('internal/modules/esm/loader');
 const path = require('path');
 const { URL } = require('url');
@@ -27,6 +31,22 @@ function initializeImportMetaObject(wrap, meta) {
   if (vmModule === undefined) {
     // This ModuleWrap belongs to the Loader.
     meta.url = wrap.url;
+    let req;
+    defineProperty(meta, 'require', {
+      enumerable: true,
+      configurable: true,
+      get() {
+        if (req !== undefined)
+          return req;
+        const url = new URL(meta.url);
+        const path = getPathFromURL(url);
+        const mod = new Module(path, null);
+        mod.filename = path;
+        mod.paths = Module._nodeModulePaths(path);
+        req = makeRequireFunction(mod).bind(null);
+        return req;
+      }
+    });
   } else {
     const initializeImportMeta = initImportMetaMap.get(vmModule);
     if (initializeImportMeta !== undefined) {
diff --git a/test/es-module/test-esm-import-meta.mjs b/test/es-module/test-esm-import-meta.mjs
index c17e0e20d49b4d..cc4d729ef9d464 100644
--- a/test/es-module/test-esm-import-meta.mjs
+++ b/test/es-module/test-esm-import-meta.mjs
@@ -3,20 +3,47 @@
 import '../common';
 import assert from 'assert';
 
+const fixtures = import.meta.require('../common/fixtures');
+
 assert.strictEqual(Object.getPrototypeOf(import.meta), null);
 
-const keys = ['url'];
+const keys = ['url', 'require'];
 assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);
 
 const descriptors = Object.getOwnPropertyDescriptors(import.meta);
+
 for (const descriptor of Object.values(descriptors)) {
   delete descriptor.value; // Values are verified below.
-  assert.deepStrictEqual(descriptor, {
-    enumerable: true,
-    writable: true,
-    configurable: true
-  });
 }
 
+assert.deepStrictEqual(descriptors.url, {
+  enumerable: true,
+  writable: true,
+  configurable: true
+});
+
+assert.deepStrictEqual(descriptors.require, {
+  get: descriptors.require.get,
+  set: undefined,
+  enumerable: true,
+  configurable: true
+});
+
+assert.strictEqual(import.meta.require.cache, undefined);
+assert.strictEqual(import.meta.require.extensions, undefined);
+assert.strictEqual(import.meta.require.main, undefined);
+assert.strictEqual(import.meta.require.paths, undefined);
+
 const urlReg = /^file:\/\/\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
 assert(import.meta.url.match(urlReg));
+
+const a = import.meta.require(
+  fixtures.path('module-require', 'relative', 'dot.js')
+);
+const b = import.meta.require(
+  fixtures.path('module-require', 'relative', 'dot-slash.js')
+);
+
+assert.strictEqual(a.value, 42);
+// require(".") should resolve like require("./")
+assert.strictEqual(a, b);