diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md
index a0662bf24bd2b03d0c2f2bde7f9d27d5c2de6191..c63600b442826dd2e43c4d18700621f9bc922014 100644
--- a/packages/react-dev-utils/README.md
+++ b/packages/react-dev-utils/README.md
@@ -236,6 +236,10 @@ var getProcessForPort = require('react-dev-utils/getProcessForPort');
 getProcessForPort(3000);
 ```
 
+#### `launchEditor(fileName: string, lineNumber: number): void`
+
+On macOS, tries to find a known running editor process and opens the file in it. It can also be explicitly configured by `REACT_EDITOR`, `VISUAL`, or `EDITOR` environment variables. For example, you can put `REACT_EDITOR=atom` in your `.env.local` file, and Create React App will respect that.
+
 #### `openBrowser(url: string): boolean`
 
 Attempts to open the browser with a given URL.<br>
diff --git a/packages/react-error-overlay/middleware.js b/packages/react-error-overlay/middleware.js
new file mode 100644
index 0000000000000000000000000000000000000000..d4fd0d399f1067b5e7ccbc0f7e3f14c572c80b5c
--- /dev/null
+++ b/packages/react-error-overlay/middleware.js
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+'use strict';
+
+const launchEditor = require('react-dev-utils/launchEditor');
+
+module.exports = function createLaunchEditorMiddleware() {
+  return function launchEditorMiddleware(req, res, next) {
+    // Keep this in sync with react-error-overlay
+    if (req.url.startsWith('/__open-stack-frame-in-editor')) {
+      launchEditor(req.query.fileName, req.query.lineNumber);
+      res.end();
+    } else {
+      next();
+    }
+  };
+};
diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json
index 6da96fc13bc0013be109760b9abdd0a972ae8abc..fed921c8f48bc2a91a8e67905eeb94bc355953a5 100644
--- a/packages/react-error-overlay/package.json
+++ b/packages/react-error-overlay/package.json
@@ -27,7 +27,8 @@
   ],
   "author": "Joe Haddad <timer150@gmail.com>",
   "files": [
-    "lib/"
+    "lib/",
+    "middleware.js"
   ],
   "dependencies": {
     "anser": "1.2.5",
diff --git a/packages/react-error-overlay/src/components/frame.js b/packages/react-error-overlay/src/components/frame.js
index d9475c7a57145a8ad54cc1c257a85acf29fb7504..dd86e9fb1675aab27fddb4a39c8087eac9146fd5 100644
--- a/packages/react-error-overlay/src/components/frame.js
+++ b/packages/react-error-overlay/src/components/frame.js
@@ -277,6 +277,7 @@ function createFrame(
       .indexOf(' ') !== -1;
     if (!isInternalWebpackBootstrapCode) {
       onSourceClick = () => {
+        // Keep this in sync with react-error-overlay/middleware.js
         fetch(
           '/__open-stack-frame-in-editor?fileName=' +
             window.encodeURIComponent(sourceFileName) +
diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js
index a7a0f7b328467893e22a07329313fe4cda40caa9..8d7eb0f1b5eb8bac45d7fd568f6cc39168190aeb 100644
--- a/packages/react-scripts/config/webpackDevServer.config.js
+++ b/packages/react-scripts/config/webpackDevServer.config.js
@@ -10,7 +10,7 @@
 // @remove-on-eject-end
 'use strict';
 
-const launchEditor = require('react-dev-utils/launchEditor');
+const errorOverlayMiddleware = require('react-error-overlay/middleware');
 const config = require('./webpack.config.dev');
 const paths = require('./paths');
 
@@ -70,15 +70,8 @@ module.exports = function(proxy, allowedHost) {
     public: allowedHost,
     proxy,
     setup(app) {
-      // This lets us open files from the crash overlay.
-      app.use(function launchEditorMiddleware(req, res, next) {
-        if (req.url.startsWith('/__open-stack-frame-in-editor')) {
-          launchEditor(req.query.fileName, req.query.lineNumber);
-          res.end();
-        } else {
-          next();
-        }
-      });
+      // This lets us open files from the runtime error overlay.
+      app.use(errorOverlayMiddleware());
     },
   };
 };