Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to Generate Proper Error Boundaries with Cursor

Cursor can generate fully typed React 18 error boundary components when you give it explicit instructions via .cursorrules and a precise Cmd+K or Chat prompt. Include the component tree structure, error state shape, and fallback UI requirements. With the right prompt, Cursor produces class-based boundaries with TypeScript generics, a reset mechanism, and an optional logging hook — saving you from runtime white-screen crashes.

What you'll learn

  • How to prompt Cursor to generate typed React 18 error boundary class components
  • How to configure .cursorrules so every boundary includes TypeScript generics and a reset handler
  • How to wrap specific component subtrees with the generated boundary using Composer
  • How to add an error-reporting hook inside the boundary for production logging
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read15-20 minCursor Free+, React 18, TypeScript 5+March 2026RapidDev Engineering Team
TL;DR

Cursor can generate fully typed React 18 error boundary components when you give it explicit instructions via .cursorrules and a precise Cmd+K or Chat prompt. Include the component tree structure, error state shape, and fallback UI requirements. With the right prompt, Cursor produces class-based boundaries with TypeScript generics, a reset mechanism, and an optional logging hook — saving you from runtime white-screen crashes.

Generate typed React 18 error boundaries with Cursor

React error boundaries must still be class components in React 18 — hooks cannot catch render-phase errors. Without explicit guidance, Cursor sometimes generates functional components that silently skip the getDerivedStateFromError lifecycle, leaving crashes unhandled. This tutorial shows you how to lock Cursor to the correct pattern: a class-based ErrorBoundary with typed props and state, a fallback render prop, a reset callback, and an optional onError hook for Sentry or similar services. You will configure .cursorrules so the constraint is permanent, then use Chat (Cmd+L) and Composer (Cmd+I) to wrap specific subtrees automatically.

Prerequisites

  • Cursor installed (Free plan or above)
  • A React 18 project with TypeScript configured
  • Basic familiarity with Cursor Chat (Cmd+L) and .cursorrules
  • React Router or similar already set up if you want route-level boundaries

Step-by-step guide

1

Add error boundary rules to .cursorrules

Open or create .cursorrules in your project root. This file is read by Cursor for every prompt, so rules here propagate automatically. Add a section that prohibits functional error boundaries and mandates TypeScript generics on state and props. Be explicit: Cursor responds better to 'never use functional components for error boundaries' than to vague style preferences. Save the file — Cursor picks it up without a restart.

.cursorrules
1# .cursorrules
2
3## React Error Boundaries
4- Error boundaries MUST be class components implementing getDerivedStateFromError and componentDidCatch.
5- NEVER generate functional error boundaries React 18 does not support them.
6- Always type ErrorBoundaryProps with a 'fallback' render prop: (error: Error, reset: () => void) => React.ReactNode
7- Always type ErrorBoundaryState as { hasError: boolean; error: Error | null }
8- Include a 'resetError' method that sets state back to { hasError: false, error: null }
9- Call props.onError(error, info) inside componentDidCatch if the prop is provided
10- Export the component as a named export, not default

Pro tip: Put the error boundary rules under a clearly labeled heading in .cursorrules. Cursor uses headings to prioritise context when the file is long.

Expected result: Every future Cursor suggestion involving error boundaries will use the class-based pattern with typed state and props.

2

Generate the base ErrorBoundary component with Chat

Open Cursor Chat with Cmd+L. Type the prompt below exactly. The @codebase reference lets Cursor scan your existing component structure so it can match your naming and import conventions. The prompt specifies the file path, TypeScript strictness, and all required lifecycle methods. Review the generated diff before accepting — check that the fallback prop is typed correctly and that resetError appears as a bound method.

Cursor Chat prompt (Cmd+L)
1Generate a typed React 18 error boundary at src/components/ErrorBoundary.tsx.
2Requirements:
3- Class component, strict TypeScript, no 'any'
4- Props: { fallback: (error: Error, reset: () => void) => React.ReactNode; onError?: (error: Error, info: React.ErrorInfo) => void; children: React.ReactNode }
5- State: { hasError: boolean; error: Error | null }
6- Implement getDerivedStateFromError and componentDidCatch
7- componentDidCatch must call this.props.onError if provided
8- Include a resetError arrow method
9- Use named export

Pro tip: If Cursor generates a functional component instead, type 'This must be a class component — React 18 does not support functional error boundaries' as a follow-up. The reminder in context usually fixes it instantly.

Expected result: Cursor creates src/components/ErrorBoundary.tsx with typed class, lifecycle methods, and reset handler.

3

Wrap a route-level subtree using Composer

Open Composer with Cmd+I. Reference the page component you want to protect using @file syntax. Ask Cursor to wrap the JSX return in your ErrorBoundary, providing a styled fallback UI that shows the error message and a 'Try again' button that calls reset. Composer can edit multiple files in one pass — it will update the import in the page file and add the boundary wrapper automatically. Review both file diffs before clicking Accept All.

Cursor Composer prompt (Cmd+I)
1Using @file src/pages/Dashboard.tsx and @file src/components/ErrorBoundary.tsx:
2
3Wrap the entire JSX returned from Dashboard in <ErrorBoundary>.
4The fallback prop should render a centered div with:
5- An h2 reading 'Something went wrong'
6- A <p> showing error.message
7- A <button onClick={reset}>Try again</button> styled with Tailwind

Pro tip: Use Composer Agent mode (toggle in the Composer panel) when you have more than two files to update. Agent mode can create the boundary file and update all usage sites in a single run.

Expected result: Dashboard.tsx now imports ErrorBoundary and wraps its return JSX with the boundary and a styled fallback.

4

Add a production error-logging hook

Select just the componentDidCatch method body in ErrorBoundary.tsx, then press Cmd+K. Use the inline edit to instruct Cursor to add a call to an external reporting function. This keeps the boundary decoupled — the hook accepts the error and info but can be swapped for Sentry, Datadog, or a custom endpoint. Cmd+K only edits the selected code, so the rest of the file stays untouched.

Cursor Cmd+K inline prompt
1Extend componentDidCatch to also call logErrorToService(error, info.componentStack).
2logErrorToService should be imported from src/utils/errorLogger.ts.
3Create that file if it does not exist: export it as an async function that accepts (error: Error, stack: string | null | undefined) => Promise<void> and currently just console.errors in development (NODE_ENV check).

Pro tip: After Cursor creates errorLogger.ts, open it and press Cmd+K again to replace the console.error stub with a real fetch call to your logging endpoint — Cursor will keep the TypeScript types intact.

Expected result: componentDidCatch calls logErrorToService. A new src/utils/errorLogger.ts file is created with a typed, environment-aware stub.

5

Verify and test the boundary

Open Cursor Chat and ask it to generate a minimal test component that deliberately throws inside render, then add a Jest + React Testing Library test that mounts it inside ErrorBoundary and asserts the fallback renders. Paste the test file path back into Chat with @file so Cursor can check the imports match your project's test setup. Run the tests from your terminal — if the boundary is correctly implemented, the 'Something went wrong' fallback text should appear in the test output.

Cursor Chat prompt (Cmd+L)
1Generate two files:
21. src/components/__tests__/ErrorBoundary.test.tsx
3 - Uses @testing-library/react and @testing-library/jest-dom
4 - Creates a ThrowingComponent that throws new Error('test error') on render
5 - Wraps it in <ErrorBoundary fallback={(err, reset) => <button onClick={reset}>{err.message}</button>}>
6 - Asserts fallback renders 'test error'
7 - Asserts clicking the button removes the fallback (reset works)
82. No separate ThrowingComponent file define it inline in the test

Pro tip: React Testing Library suppresses console.error for expected errors. Add jest.spyOn(console, 'error').mockImplementation(() => {}) in a beforeEach block to keep test output clean.

Expected result: Test file is generated. Running Jest shows two passing tests: fallback renders and reset restores children.

Complete working example

src/components/ErrorBoundary.tsx
1import React from 'react';
2import { logErrorToService } from '../utils/errorLogger';
3
4interface ErrorBoundaryProps {
5 fallback: (error: Error, reset: () => void) => React.ReactNode;
6 onError?: (error: Error, info: React.ErrorInfo) => void;
7 children: React.ReactNode;
8}
9
10interface ErrorBoundaryState {
11 hasError: boolean;
12 error: Error | null;
13}
14
15export class ErrorBoundary extends React.Component<
16 ErrorBoundaryProps,
17 ErrorBoundaryState
18> {
19 constructor(props: ErrorBoundaryProps) {
20 super(props);
21 this.state = { hasError: false, error: null };
22 this.resetError = this.resetError.bind(this);
23 }
24
25 static getDerivedStateFromError(error: Error): ErrorBoundaryState {
26 return { hasError: true, error };
27 }
28
29 componentDidCatch(error: Error, info: React.ErrorInfo): void {
30 if (this.props.onError) {
31 this.props.onError(error, info);
32 }
33 void logErrorToService(error, info.componentStack);
34 }
35
36 resetError(): void {
37 this.setState({ hasError: false, error: null });
38 }
39
40 render(): React.ReactNode {
41 const { hasError, error } = this.state;
42
43 if (hasError && error) {
44 return this.props.fallback(error, this.resetError);
45 }
46
47 return this.props.children;
48 }
49}
50
51// src/utils/errorLogger.ts
52// export async function logErrorToService(
53// error: Error,
54// stack: string | null | undefined
55// ): Promise<void> {
56// if (process.env.NODE_ENV === 'development') {
57// console.error('[ErrorBoundary]', error.message, stack);
58// return;
59// }
60// await fetch('/api/log-error', {
61// method: 'POST',
62// headers: { 'Content-Type': 'application/json' },
63// body: JSON.stringify({ message: error.message, stack }),
64// });
65// }

Common mistakes when generating Proper Error Boundaries with Cursor

Why it's a problem: Asking Cursor to 'add an error boundary' without specifying class vs functional

How to avoid: Always include 'class component' and 'getDerivedStateFromError' in your prompt. Add the constraint to .cursorrules so it applies to every session.

Why it's a problem: Forgetting to bind resetError in the constructor

How to avoid: Ask Cursor to bind all methods in the constructor, or use arrow function class properties. Add a note to .cursorrules: 'Bind all class methods in constructor or use arrow properties.'

Why it's a problem: Placing the error boundary at the very top of the component tree only

How to avoid: Use Composer to add boundaries at route level and around critical widgets. Prompt Cursor: 'Add an ErrorBoundary wrapper around each route in src/App.tsx with a route-specific fallback.'

Why it's a problem: Not testing that reset actually restores children

How to avoid: Generate the test described in Step 5. The test that clicks reset and asserts children re-appear is the fastest way to catch this bug.

Best practices

  • Define ErrorBoundaryProps and ErrorBoundaryState as separate named interfaces, never inline — this makes the types reusable across multiple boundary instances.
  • Always provide an onError prop hook so callers can plug in Sentry, Datadog, or a custom endpoint without modifying the boundary component itself.
  • Add route-level boundaries around every major page and feature-level boundaries around high-risk widgets like data tables and charts.
  • Keep the fallback UI self-contained with no external data dependencies — it must render even if the rest of the app is broken.
  • Store the error in component state so the fallback can display a user-friendly message derived from error.message.
  • Use the getDerivedStateFromError static method for state updates and componentDidCatch for side effects (logging) — mixing the two in one method is a common anti-pattern.
  • Add the boundary pattern to .cursorrules once and never repeat it in individual prompts — consistent rules across all sessions prevent drift.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a fully typed React 18 ErrorBoundary class component in TypeScript. It should have a 'fallback' render prop typed as (error: Error, reset: () => void) => React.ReactNode, an optional onError callback, getDerivedStateFromError, componentDidCatch that calls onError if provided, and a resetError method that clears state. Use strict TypeScript with no 'any'.

Cursor Prompt

In Cursor Chat (Cmd+L), type: 'Generate a typed React 18 error boundary at src/components/ErrorBoundary.tsx. Class component, strict TypeScript. Props: { fallback: (error: Error, reset: () => void) => React.ReactNode; onError?: (error: Error, info: React.ErrorInfo) => void; children: React.ReactNode }. State: { hasError: boolean; error: Error | null }. Implement getDerivedStateFromError and componentDidCatch. componentDidCatch calls this.props.onError if provided. Include resetError arrow method. Named export.'

Frequently asked questions

Why does Cursor sometimes generate a functional component when I ask for an error boundary?

Without explicit constraints, Cursor picks the most common modern React pattern, which is functional. Add 'Error boundaries MUST be class components' to .cursorrules and include 'class component with getDerivedStateFromError' in every related prompt.

Can I use the same ErrorBoundary component at multiple levels of my component tree?

Yes. The component is stateless between instances, so you can place it at the route level, around individual widgets, or both. Each instance maintains its own hasError state independently.

Does Cursor's Tab completion respect my .cursorrules when it autocompletes JSX with error boundaries?

Tab completion uses nearby code as context, not .cursorrules directly. After you have the typed ErrorBoundary component in your project, Tab will infer the correct prop shape from the TypeScript definitions automatically.

How do I get Cursor to add error boundaries to an existing codebase with many routes?

Open Composer (Cmd+I) in Agent mode and prompt: 'Find every top-level route component under src/pages and wrap its JSX return in <ErrorBoundary> imported from src/components/ErrorBoundary.tsx. Use a generic fallback for now.' Agent mode will edit multiple files in one pass.

Should the fallback UI fetch any data or use React hooks?

No. The fallback renders inside a broken subtree, so any hook that depends on context or external state may also fail. Keep the fallback purely presentational — static markup, the error message string, and the reset button only.

Can I generate error boundaries for React Native with the same Cursor prompts?

Yes, with small changes. Replace React.ReactNode with React.ReactElement in the fallback type, and swap Tailwind class names for React Native StyleSheet. Add 'This is a React Native project — use StyleSheet, not Tailwind' to your prompt.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.