@@ -734,12 +772,13 @@ pre.nowrap {
```
```js src/reportError.js hidden
-function reportError({ title, error, componentStack, dismissable }) {
+function reportError({ title, error, componentStack, ownerStack, dismissable }) {
const errorDialog = document.getElementById("error-dialog");
const errorTitle = document.getElementById("error-title");
const errorMessage = document.getElementById("error-message");
const errorBody = document.getElementById("error-body");
const errorComponentStack = document.getElementById("error-component-stack");
+ const errorOwnerStack = document.getElementById("error-owner-stack");
const errorStack = document.getElementById("error-stack");
const errorClose = document.getElementById("error-close");
const errorCause = document.getElementById("error-cause");
@@ -762,6 +801,9 @@ function reportError({ title, error, componentStack, dismissable }) {
// Display component stack
errorComponentStack.innerText = componentStack;
+ // Display owner stack
+ errorOwnerStack.innerText = ownerStack;
+
// Display the call stack
// Since we already displayed the message, strip it, and the first Error: line.
errorStack.innerText = error.stack.replace(error.message, '').split(/\n(.*)/s)[1];
@@ -787,32 +829,35 @@ function reportError({ title, error, componentStack, dismissable }) {
errorDialog.classList.remove("hidden");
}
-export function reportCaughtError({error, cause, componentStack}) {
- reportError({ title: "Caught Error", error, componentStack, dismissable: true});
+export function reportCaughtError({error, cause, componentStack, ownerStack}) {
+ reportError({ title: "Caught Error", error, componentStack, ownerStack, dismissable: true});
}
export function reportUncaughtError({error, cause, componentStack}) {
- reportError({ title: "Uncaught Error", error, componentStack, dismissable: false });
+ reportError({ title: "Uncaught Error", error, componentStack, ownerStack, dismissable: false });
}
export function reportRecoverableError({error, cause, componentStack}) {
- reportError({ title: "Recoverable Error", error, componentStack, dismissable: true });
+ reportError({ title: "Recoverable Error", error, componentStack, ownerStack, ownerStack, dismissable: true });
}
```
```js src/index.js active
+// captureOwnerStack is only available in react@experimental.
+import {captureOwnerStack} from 'react';
import { hydrateRoot } from "react-dom/client";
import App from "./App.js";
import {reportCaughtError} from "./reportError";
import "./styles.css";
const container = document.getElementById("root");
-const root = hydrateRoot(container,
, {
+hydrateRoot(container,
, {
onCaughtError: (error, errorInfo) => {
if (error.message !== 'Known error') {
reportCaughtError({
error,
- componentStack: errorInfo.componentStack
+ componentStack: errorInfo.componentStack,
+ ownerStack: captureOwnerStack()
});
}
}
@@ -879,12 +924,11 @@ function Throw({error}) {
```json package.json hidden
{
"dependencies": {
- "react": "19.0.0-rc-3edc000d-20240926",
- "react-dom": "19.0.0-rc-3edc000d-20240926",
+ "react": "experimental",
+ "react-dom": "experimental",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
- },
- "main": "/index.js"
+ }
}
```
@@ -894,7 +938,9 @@ function Throw({error}) {
When React encounters a hydration mismatch, it will automatically attempt to recover by rendering on the client. By default, React will log hydration mismatch errors to `console.error`. To override this behavior, you can provide the optional `onRecoverableError` root option:
-```js [[1, 7, "onRecoverableError"], [2, 7, "error", 1], [3, 11, "error.cause", 1], [4, 7, "errorInfo"], [5, 12, "componentStack"]]
+```js [[1, 9, "onRecoverableError"], [2, 9, "error", 1], [3, 13, "error.cause", 1], [4, 9, "errorInfo"], [5, 14, "componentStack"], [6, 15, "captureOwnerStack()"]]
+// captureOwnerStack is only available in react@experimental.
+import { captureOwnerStack } from 'react';
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
@@ -906,7 +952,8 @@ const root = hydrateRoot(
'Caught error',
error,
error.cause,
- errorInfo.componentStack
+ errorInfo.componentStack,
+ captureOwnerStack()
);
}
}
@@ -918,6 +965,8 @@ The
onRecoverableError option is a function called
1. The
error React throws. Some errors may include the original cause as
error.cause.
2. An
errorInfo object that contains the
componentStack of the error.
+
With
[`captureOwnerStack`](/reference/react/captureOwnerStack) you can include the Owner Stack during development.
+
You can use the `onRecoverableError` root option to display error dialogs for hydration mismatches:
@@ -941,8 +990,10 @@ You can use the `onRecoverableError` root option to display error dialogs for hy
- This error occurred at:
+ Component stack:
+ Owner stack:
+
Call stack:
@@ -1015,12 +1066,13 @@ pre.nowrap {
```
```js src/reportError.js hidden
-function reportError({ title, error, componentStack, dismissable }) {
+function reportError({ title, error, componentStack, ownerStack, dismissable }) {
const errorDialog = document.getElementById("error-dialog");
const errorTitle = document.getElementById("error-title");
const errorMessage = document.getElementById("error-message");
const errorBody = document.getElementById("error-body");
const errorComponentStack = document.getElementById("error-component-stack");
+ const errorOwnerStack = document.getElementById("error-owner-stack");
const errorStack = document.getElementById("error-stack");
const errorClose = document.getElementById("error-close");
const errorCause = document.getElementById("error-cause");
@@ -1043,6 +1095,9 @@ function reportError({ title, error, componentStack, dismissable }) {
// Display component stack
errorComponentStack.innerText = componentStack;
+ // Display owner stack
+ errorOwnerStack.innerText = ownerStack;
+
// Display the call stack
// Since we already displayed the message, strip it, and the first Error: line.
errorStack.innerText = error.stack.replace(error.message, '').split(/\n(.*)/s)[1];
@@ -1068,20 +1123,22 @@ function reportError({ title, error, componentStack, dismissable }) {
errorDialog.classList.remove("hidden");
}
-export function reportCaughtError({error, cause, componentStack}) {
- reportError({ title: "Caught Error", error, componentStack, dismissable: true});
+export function reportCaughtError({error, cause, componentStack, ownerStack}) {
+ reportError({ title: "Caught Error", error, componentStack, ownerStack, dismissable: true});
}
-export function reportUncaughtError({error, cause, componentStack}) {
- reportError({ title: "Uncaught Error", error, componentStack, dismissable: false });
+export function reportUncaughtError({error, cause, componentStack, ownerStack}) {
+ reportError({ title: "Uncaught Error", error, componentStack, ownerStack, dismissable: false });
}
-export function reportRecoverableError({error, cause, componentStack}) {
- reportError({ title: "Recoverable Error", error, componentStack, dismissable: true });
+export function reportRecoverableError({error, cause, componentStack, ownerStack}) {
+ reportError({ title: "Recoverable Error", error, componentStack, ownerStack, dismissable: true });
}
```
```js src/index.js active
+// captureOwnerStack is only available in react@experimental.
+import {captureOwnerStack} from 'react'
import { hydrateRoot } from "react-dom/client";
import App from "./App.js";
import {reportRecoverableError} from "./reportError";
@@ -1093,7 +1150,8 @@ const root = hydrateRoot(container,
, {
reportRecoverableError({
error,
cause: error.cause,
- componentStack: errorInfo.componentStack
+ componentStack: errorInfo.componentStack,
+ ownerStack: captureOwnerStack()
});
}
});
@@ -1104,16 +1162,6 @@ import { useState } from 'react';
import { ErrorBoundary } from "react-error-boundary";
export default function App() {
- const [error, setError] = useState(null);
-
- function handleUnknown() {
- setError("unknown");
- }
-
- function handleKnown() {
- setError("known");
- }
-
return (
{typeof window !== 'undefined' ? 'Client' : 'Server'}
);
@@ -1141,8 +1189,8 @@ function Throw({error}) {
```json package.json hidden
{
"dependencies": {
- "react": "19.0.0-rc-3edc000d-20240926",
- "react-dom": "19.0.0-rc-3edc000d-20240926",
+ "react": "experimental",
+ "react-dom": "experimental",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
},
diff --git a/src/content/reference/react/Component.md b/src/content/reference/react/Component.md
index 99fa17986f0..5e8c8d9679f 100644
--- a/src/content/reference/react/Component.md
+++ b/src/content/reference/react/Component.md
@@ -1273,7 +1273,11 @@ By default, if your application throws an error during rendering, React will rem
To implement an error boundary component, you need to provide [`static getDerivedStateFromError`](#static-getderivedstatefromerror) which lets you update state in response to an error and display an error message to the user. You can also optionally implement [`componentDidCatch`](#componentdidcatch) to add some extra logic, for example, to log the error to an analytics service.
-```js {7-10,12-19}
+
With [`captureOwnerStack`](/reference/react/captureOwnerStack) you can include the Owner Stack during development.
+
+```js {9-12,14-27}
+import * as React from 'react';
+
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
@@ -1286,12 +1290,18 @@ class ErrorBoundary extends React.Component {
}
componentDidCatch(error, info) {
- // Example "componentStack":
- // in ComponentThatThrows (created by App)
- // in ErrorBoundary (created by App)
- // in div (created by App)
- // in App
- logErrorToMyService(error, info.componentStack);
+ logErrorToMyService(
+ error,
+ // Example "componentStack":
+ // in ComponentThatThrows (created by App)
+ // in ErrorBoundary (created by App)
+ // in div (created by App)
+ // in App
+ info.componentStack,
+ // Only available in react@experimental.
+ // Warning: Owner Stack is not available in production.
+ React.captureOwnerStack(),
+ );
}
render() {
diff --git a/src/content/reference/react/captureOwnerStack.md b/src/content/reference/react/captureOwnerStack.md
index 16694b2117b..0afcceefdc1 100644
--- a/src/content/reference/react/captureOwnerStack.md
+++ b/src/content/reference/react/captureOwnerStack.md
@@ -184,6 +184,15 @@ Neither `Navigation` nor `legend` are in the stack at all since it's only a sibl
## Usage {/*usage*/}
+### Show an error dialog {/*show-an-error-dialog*/}
+
+Check out the following example to see how to use `captureOwnerStack` to improve an error dialog:
+
+- [createRoot usage: Show a dialog for uncaught errors
+](/reference/react-dom/client/hydrateRoot#updating-a-hydrated-root-component)
+- [createRoot usage: Displaying Error Boundary errors](/reference/react-dom/client/createRoot#displaying-error-boundary-errors)
+- [createRoot usage: Displaying a dialog for recoverable errors](/reference/react-dom/client/createRoot#displaying-a-dialog-for-recoverable-errors)
+
### Expanding error stacks {/*expanding-error-stacks*/}
In addition to the stack trace of the
error itself, you can use
`captureOwnerStack` to append the Owner Stack.