A development tool that tracks which React Suspense boundary catches thrown promises for easier debugging
npm install react-swc-suspense-tracker

A development tool that helps you track which React Suspense and Error boundaries handle thrown promises and errors, making it easier to debug your React applications that use Suspense for data fetching and Error boundaries for error handling.
!Screenshot of React Dev Tools with Suspense Boundary Information
React Suspense uses thrown Promises to pause rendering until data is ready, and Error boundaries catch thrown errors to handle failures gracefully. However, there's no public API to identify which boundary catches a suspension or error.
This makes debugging difficult when you have multiple nested boundaries and need to understand the flow of suspensions and errors in your app.
This package provides:
- SWC Plugin: Automatically replaces Suspense and ErrorBoundary imports to use trackable versions
- Development Hooks: Utilities to detect missing boundaries and debug suspension/error flow
``bash`
npm install --save-dev react-swc-suspense-tracker
Add the plugin to your next.config.js:
`javascript`
module.exports = {
experimental: {
swcPlugins: [
[
"react-swc-suspense-tracker/swc",
{
// Optional: track custom boundary components
boundaries: [
{ component: "ErrorBoundary", from: "react-error-boundary" }
]
}
],
],
},
};
*Note: The plugin is enabled by default in development mode and disabled in production builds. You can override this behavior by setting the enabled option.
#### Plugin Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | true on developmentfalse
in production | Enable/disable the plugin transformation |boundaries
| | Array<{component: string, from: string}> | [] | Additional boundary components to track (e.g., custom Error boundaries) |
#### Using with SWC directly
Add to your .swcrc:
`json`
{
"jsc": {
"experimental": {
"plugins": [
[
"react-swc-suspense-tracker/swc",
{
"boundaries": [
{ "component": "ErrorBoundary", "from": "react-error-boundary" }
]
}
]
]
}
}
}
The following example shows how you can debug specific hooks that might suspend.
Note: By default suspenseInfo will always be null in production mode.enabled
To change that you have to set the option to true in the SWC plugin configuration.
`tsx
import { useQuery } from 'react-query';
import { wrapSuspendableHook } from 'react-swc-suspense-tracker';
const useQueryWithDebug = process.env.NODE_ENV === 'production'
? useQuery
: wrapSuspendableHook(
useQuery,
(suspenseBoundaries, queryKey) => {
if (suspenseBoundaries.length === 0) {
console.warn(Suspense triggered by ${queryKey} but no Suspense boundary found);Suspense triggered by ${queryKey} for boundary: ${suspenseBoundaries[0]}
} else {
console.info();
}
}
);
function MyComponent() {
const { data } = useQueryWithDebug('my-query-key', fetchData);
return
$3
If you want to ensure that your component is wrapped in a Suspense boundary, you can use the
useThrowIfSuspenseMissing hook.
This will throw an error in development if the component might suspend but has no Suspense boundary above it.`javascript
import { useThrowIfSuspenseMissing } from "react-swc-suspense-tracker";function MyComponent() {
// This will throw an error in development if no Suspense boundary is found
useThrowIfSuspenseMissing();
const data = useSomeDataHook(); // This might suspend
return
{data};
}
`Result:
!Simple usage screenshot of useThrowIfSuspenseMissing
$3
The plugin automatically transforms:
`javascript
// Before transformation
import { Suspense } from "react";
}>
// After transformation
import { Suspense } from "react";
import { BoundaryTrackerSWC } from "react-swc-suspense-tracker/context";
} id="my/file.tsx:123">
`$3
For custom logging or logging in production you can use the
useSuspenseOwner hook to get the ID of the nearest Suspense boundary:`javascript
import {
useSuspenseOwner,
} from "react-swc-suspense-tracker";
function MyComponent() {
const suspenseOwner = useSuspenseOwner();
// Log the Suspense boundary ID
console.log("Closest Suspense boundary ID:", suspenseOwner); const data = useSomeDataHook(); // This might suspend
return
{data};
}
`API Reference
$3
####
useSuspenseOwner(): string | nullReturns the ID of the nearest Suspense boundary above this component. The ID format is
"file.tsx:line" if set by the SWC plugin, or a custom string if set manually.Returns
null if no Suspense boundary is found.####
useBoundaryStack(): Array<[string, ComponentType]>Returns information about all boundary components above this component as an array of
[boundaryId, BoundaryComponent] tuples, ordered from outermost to innermost boundary.Returns empty array if no boundaries are found.
####
useThrowIfSuspenseMissing(skip?: boolean): voidThrows an error in development if this component might suspend but has no Suspense boundary above it - NOOP in production builds
Parameters:
-
skip (optional): If true, skips the check. Defaults to false.Throws: Error with message explaining the missing Suspense boundary.
####
wrapSuspendableHookWraps a hook to catch Suspense errors and call the provided
onSuspense function with the current Suspense boundary information.Parameters:
-
hook: The hook function to wrap
- onSuspense: Function called when the hook suspends, receives array of Suspense boundary IDsReturns: A wrapped version of the hook with the same signature.
Development vs Production
This package is designed for development use only. The SWC plugin should be disabled in production builds to avoid the additional runtime overhead.
Build
Get the Rust toolchain and the right target:
`bash
rustup target add wasm32-wasip1
npm run build
`The compiled Wasm module will be available as
react_swc_suspense_tracker.wasm`.