diff --git a/docs/reference-guides/actions/editor-actions.md b/docs/reference-guides/actions/editor-actions.md
new file mode 100644
index 0000000000000..d5e37744036ee
--- /dev/null
+++ b/docs/reference-guides/actions/editor-actions.md
@@ -0,0 +1,24 @@
+# Editor Actions
+
+To help you hook into the editor lifecycle and extend it, the following Actions are exposed:
+
+### Error Boundaries
+
+#### `editor.ErrorBoundary.errorLogged`
+
+Allows you to hook into the editor Error Boundaries' `componentDidCatch` and gives you access to the error object.
+
+You can use if you want to get hold of the error object that's handled by the boundaries, i.e to send them to an external error tracking tool.
+
+_Example_:
+
+```js
+addAction(
+ 'editor.ErrorBoundary.errorLogged',
+ 'mu-plugin/error-capture-setup',
+ ( error ) => {
+ // error is the exception's error object
+ ErrorCaptureTool.captureError( error );
+ }
+);
+```
diff --git a/packages/customize-widgets/src/components/error-boundary/index.js b/packages/customize-widgets/src/components/error-boundary/index.js
index 17047e7f546ea..d1aece49b9173 100644
--- a/packages/customize-widgets/src/components/error-boundary/index.js
+++ b/packages/customize-widgets/src/components/error-boundary/index.js
@@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Warning } from '@wordpress/block-editor';
import { useCopyToClipboard } from '@wordpress/compose';
+import { doAction } from '@wordpress/hooks';
function CopyButton( { text, children } ) {
const ref = useCopyToClipboard( text );
@@ -26,6 +27,8 @@ export default class ErrorBoundary extends Component {
componentDidCatch( error ) {
this.setState( { error } );
+
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
}
render() {
diff --git a/packages/customize-widgets/src/components/test/error-boundary.js b/packages/customize-widgets/src/components/test/error-boundary.js
new file mode 100644
index 0000000000000..7b9977e10f131
--- /dev/null
+++ b/packages/customize-widgets/src/components/test/error-boundary.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import * as wpHooks from '@wordpress/hooks';
+/**
+ * Internal dependencies
+ */
+import ErrorBoundary from '../error-boundary';
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+
+const theError = new Error( 'Kaboom' );
+
+const ChildComponent = () => {
+ throw theError;
+};
+
+describe( 'Error Boundary', () => {
+ describe( 'when error is thrown from a Child component', () => {
+ it( 'calls the `editor.ErrorBoundary.errorLogged` hook action with the error object', () => {
+ const doAction = jest.spyOn( wpHooks, 'doAction' );
+
+ render(
+
+
+
+ );
+
+ expect( doAction ).toHaveBeenCalledWith(
+ 'editor.ErrorBoundary.errorLogged',
+ theError
+ );
+ expect( console ).toHaveErrored();
+ } );
+ } );
+} );
diff --git a/packages/edit-site/src/components/error-boundary/index.js b/packages/edit-site/src/components/error-boundary/index.js
index 35387fe5399c1..9e07afafd04a4 100644
--- a/packages/edit-site/src/components/error-boundary/index.js
+++ b/packages/edit-site/src/components/error-boundary/index.js
@@ -3,6 +3,7 @@
*/
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
+import { doAction } from '@wordpress/hooks';
/**
* Internal dependencies
@@ -20,6 +21,10 @@ export default class ErrorBoundary extends Component {
};
}
+ componentDidCatch( error ) {
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
+ }
+
static getDerivedStateFromError( error ) {
return { error };
}
diff --git a/packages/edit-site/src/components/test/error-boundary.js b/packages/edit-site/src/components/test/error-boundary.js
new file mode 100644
index 0000000000000..7b9977e10f131
--- /dev/null
+++ b/packages/edit-site/src/components/test/error-boundary.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import * as wpHooks from '@wordpress/hooks';
+/**
+ * Internal dependencies
+ */
+import ErrorBoundary from '../error-boundary';
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+
+const theError = new Error( 'Kaboom' );
+
+const ChildComponent = () => {
+ throw theError;
+};
+
+describe( 'Error Boundary', () => {
+ describe( 'when error is thrown from a Child component', () => {
+ it( 'calls the `editor.ErrorBoundary.errorLogged` hook action with the error object', () => {
+ const doAction = jest.spyOn( wpHooks, 'doAction' );
+
+ render(
+
+
+
+ );
+
+ expect( doAction ).toHaveBeenCalledWith(
+ 'editor.ErrorBoundary.errorLogged',
+ theError
+ );
+ expect( console ).toHaveErrored();
+ } );
+ } );
+} );
diff --git a/packages/edit-widgets/src/components/error-boundary/index.js b/packages/edit-widgets/src/components/error-boundary/index.js
index cf2098a30844d..8b941630b347c 100644
--- a/packages/edit-widgets/src/components/error-boundary/index.js
+++ b/packages/edit-widgets/src/components/error-boundary/index.js
@@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Warning } from '@wordpress/block-editor';
import { useCopyToClipboard } from '@wordpress/compose';
+import { doAction } from '@wordpress/hooks';
function CopyButton( { text, children } ) {
const ref = useCopyToClipboard( text );
@@ -29,6 +30,8 @@ export default class ErrorBoundary extends Component {
componentDidCatch( error ) {
this.setState( { error } );
+
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
}
reboot() {
diff --git a/packages/edit-widgets/src/components/test/error-boundary.js b/packages/edit-widgets/src/components/test/error-boundary.js
new file mode 100644
index 0000000000000..7b9977e10f131
--- /dev/null
+++ b/packages/edit-widgets/src/components/test/error-boundary.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import * as wpHooks from '@wordpress/hooks';
+/**
+ * Internal dependencies
+ */
+import ErrorBoundary from '../error-boundary';
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+
+const theError = new Error( 'Kaboom' );
+
+const ChildComponent = () => {
+ throw theError;
+};
+
+describe( 'Error Boundary', () => {
+ describe( 'when error is thrown from a Child component', () => {
+ it( 'calls the `editor.ErrorBoundary.errorLogged` hook action with the error object', () => {
+ const doAction = jest.spyOn( wpHooks, 'doAction' );
+
+ render(
+
+
+
+ );
+
+ expect( doAction ).toHaveBeenCalledWith(
+ 'editor.ErrorBoundary.errorLogged',
+ theError
+ );
+ expect( console ).toHaveErrored();
+ } );
+ } );
+} );
diff --git a/packages/editor/src/components/error-boundary/index.js b/packages/editor/src/components/error-boundary/index.js
index c7d0824a3dee2..72e6a7680421f 100644
--- a/packages/editor/src/components/error-boundary/index.js
+++ b/packages/editor/src/components/error-boundary/index.js
@@ -7,6 +7,7 @@ import { Button } from '@wordpress/components';
import { select } from '@wordpress/data';
import { Warning } from '@wordpress/block-editor';
import { useCopyToClipboard } from '@wordpress/compose';
+import { doAction } from '@wordpress/hooks';
/**
* Internal dependencies
@@ -36,6 +37,8 @@ class ErrorBoundary extends Component {
componentDidCatch( error ) {
this.setState( { error } );
+
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
}
reboot() {