captureOwnerStack

Returns the current Owner Stack as a string in development builds, for richer error logging.

Signature
const stack = captureOwnerStack()
  • Added in React 19.1.0

Return value

string | nullThe current Owner Stack as a string, or null if no Owner Stack is available. Owner Stacks are available during component render, in Effects such as useEffect, in React's event handlers such as <button onClick>, and in the React root error handlers onCaughtError, onRecoverableError and onUncaughtError.

Errors

Calling it as a named import in a production bundle throws a TypeError, because the export is `undefined` there — access it through a namespace import guarded by a `process.env.NODE_ENV` check.

Changelog

  • PHP 19.1.0Introduced. Available in development mode only, returning an Owner Stack when one is available.

A component stack answers where is this component in the tree. An Owner Stack answers a different and usually more useful question: which component actually created this element. captureOwnerStack reads that stack in development and returns it as a string so you can attach it to your own logs.

When to reach for it

Reach for it when a warning or error names a leaf that hundreds of places render — a missing key in a shared <List>, an invalid prop on a design-system <Button>, a bad nesting warning inside a layout primitive. The component stack shows the wrappers between root and leaf; the Owner Stack shows the file that wrote the JSX. The natural place to call it is inside createRoot's onCaughtError, onUncaughtError and onRecoverableError handlers, or in an error-boundary componentDidCatch, before forwarding to your error reporter.

Development only, and that is load-bearing

Owner Stacks exist only in development builds, and captureOwnerStack is not even exported from production React. A named import in a file that ships to production leaves you calling undefined. Use a namespace import and guard the call. A null return simply means no Owner Stack was available at that point — you are outside render, an effect, a React event handler or a React error handler.

Example

Example · jsx
// index.jsx — development-mode error reporting
import * as React from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";

function ownerStack() {
  // Named imports break in production: the export does not exist there.
  if (process.env.NODE_ENV !== "production") {
    return React.captureOwnerStack();
  }
  return null;
}

const root = createRoot(document.getElementById("root"), {
  onCaughtError(error, errorInfo) {
    console.group(`Caught: ${error.message}`);
    console.log("Component stack:", errorInfo.componentStack);
    console.log("Owner stack:", ownerStack());
    console.groupEnd();
  },
  onUncaughtError(error, errorInfo) {
    console.error(error, errorInfo.componentStack, ownerStack());
  },
  onRecoverableError(error) {
    console.warn("Recovered:", error.message, ownerStack());
  }
});

root.render(<App />);

When to use it

  • Attach the owning component's stack to errors forwarded from onCaughtError to an error-reporting service.
  • Track down which call site rendered the list that is missing keys, when the list component is used in fifty places.
  • Log the owner of a component that received an invalid prop from a shared wrapper.

Frequently asked questions

Why is captureOwnerStack undefined?

Because you are running a production build, where React does not export it at all. Use a namespace import — import * as React from 'react' — and call React.captureOwnerStack() only inside a process.env.NODE_ENV !== 'production' check, so bundlers can strip the call from production output.

Why does captureOwnerStack return null?

Because no Owner Stack is available at the call site. Owner Stacks exist during component render, inside effects, inside React event handlers, and inside the root error handlers onCaughtError, onUncaughtError and onRecoverableError. Calling it from a setTimeout, a promise callback or a module's top level returns null, as does any call in a production build.

What is the difference between an owner stack and a component stack?

A component stack lists the ancestors of a component in the rendered tree; an Owner Stack names the components that created the JSX elements, which is the call site you actually need to edit. They diverge whenever elements are passed as children or props, where the parent in the tree is not the code that wrote the element.

More examples

Owner stack versus component stack

The component stack for Badge would read Badge → Row → Toolbar → App: every ancestor in the rendered tree. The Owner Stack instead names Row — the component whose JSX actually created the <Badge> element — which is the file you need to open. The two differ whenever elements are passed around as children or props rather than created in place.

Example · jsx
import * as React from "react";

function Badge({ children }) {
  // Called during render: an Owner Stack is available here.
  if (process.env.NODE_ENV !== "production") {
    console.log("Badge was rendered by:\n" + React.captureOwnerStack());
  }
  return <span>{children}</span>;
}

function Row({ label }) {
  return (
    <li>
      <Badge>{label}</Badge>
    </li>
  );
}

function Toolbar() {
  return (
    <ul>
      <Row label="new" />
    </ul>
  );
}

export default function App() {
  return <Toolbar />;
}

Adding the owner stack to an error boundary report

In development the telemetry payload carries both stacks: componentStack locates the failure in the rendered tree, ownerStack names the component that wrote the JSX for the thrown-from element. In a production build the ownerStack field is null and no import error occurs, because the API is reached through the React namespace behind a NODE_ENV guard rather than as a named import.

Example · jsx
import * as React from "react";
import { Component } from "react";
import { reportError } from "./telemetry";

class ErrorBoundary extends Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  componentDidCatch(error, info) {
    reportError({
      message: error.message,
      componentStack: info.componentStack,
      // undefined in production builds, so read it through the namespace
      ownerStack:
        process.env.NODE_ENV !== "production"
          ? React.captureOwnerStack()
          : null
    });
  }

  render() {
    if (this.state.error) {
      return <p>Something went wrong.</p>;
    }
    return this.props.children;
  }
}

function Broken() {
  throw new Error("boom");
}

export default function App() {
  return (
    <ErrorBoundary>
      <Broken />
    </ErrorBoundary>
  );
}

Discussion

  • Be the first to comment on this lesson.