How to Debug Custom Hooks with useDebugValue

Use useDebugValue to label a custom Hook in React DevTools, and learn to defer expensive formatting with the optional format function.

6 min read

useDebugValue adds a readable label to a custom Hook in React DevTools. It shows intent when you inspect a component, instead of forcing you to read raw internal data. The label is developer-only and never appears in the rendered UI.

Label a custom Hook

Call useDebugValue at the top level of the Hook with the value you want DevTools to show next to the Hook name.

App.jsxApp.jsx
import { useDebugValue, useState } from "react";
 
function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);
  useDebugValue(count === 0 ? "empty" : "counting");
  return [count, setCount];
}

In React DevTools, a component using this Hook now shows a label like Counter: "empty" or Counter: "counting" next to the Hook. Without the call, DevTools would show only the raw number.

The label is static text for debugging, so it does not affect rendering. Only custom Hooks may call it, so it belongs inside the Hook body, never in a component.

Defer expensive formatting

Passing a second argument turns it into a formatting function that runs only when the Hook is inspected.

App.jsxApp.jsx
useDebugValue(items, (list) => list.length + " items");

The function receives the debug value and returns the display text. Because it runs lazily, the Hook avoids calling a costly formatter on every render and only pays the cost inside DevTools.

Pass this formatter when the raw value is a large array or an object that is expensive to stringify. For simple values, skip the formatter and pass the value directly.

When useDebugValue is worth it

Do not add a debug value to every Hook. It matters most for shared library Hooks whose internal state is large or hard to inspect, where a short label saves the next developer real time.

  • Use a short, human-readable label over a raw object.
  • Reach for the format function only when formatting is expensive.
  • Skip it when the underlying value is already obvious.

A label is developer tooling, not part of the public API of the Hook. The label should answer a question at a glance, not dump every field of the Hook. A good label reads like a status word rather than a data dump.

See the label in DevTools

Open React DevTools and select a component that calls the Hook. The label appears in the Hooks panel beside the Hook name, and it updates on every render like the values around it. The first argument is evaluated on every render, so keep it cheap and reserve the formatter for anything heavy.

See how to create a custom Hook for the extraction steps, and React Developer Tools for where the label appears.

Rune AI

Rune AI

Key Insights

  • Call useDebugValue at the top level of a custom Hook.
  • The first argument is the value shown in DevTools.
  • The optional second argument is a formatting function.
  • Format functions run only when the Hook is inspected.
  • Use it for shared Hooks with complex internal state.
RunePowered by Rune AI

Frequently Asked Questions

What does useDebugValue do?

It adds a readable label to a custom Hook in React DevTools. Without it, DevTools shows the raw underlying value instead.

Where can useDebugValue be called?

Only inside a custom Hook, at the top level. It is not for components or regular functions.

When should I skip useDebugValue?

Skip it for simple Hooks where the value is already obvious. It pays off for shared library Hooks with complex internal data.

Conclusion

useDebugValue gives a custom Hook a readable label in React DevTools, so inspecting a component shows intent instead of raw data. Pass a format function to defer expensive formatting until the Hook is actually inspected.