Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Full Sentry SDK setup for React. Use when asked to \"add Sentry to React\", \"install @sentry/react\", or configure error monitoring, tracing, session replay, profiling, or logging for React applications. Supports React 16+, React Router v5-v7 non-framework mode, TanStack Router, Redux, Vite, and webpack.
.claude/skills/kunanonj-cursor-plugin-sentry-sentry-react-sdk/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 145% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 163% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 121% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 202% | 0% |
| case-04 | ✓→✗ | ▼ Worse | 77% | 0% |
> All Skills > SDK Setup > React SDK
Opinionated wizard that scans your React project and guides you through complete Sentry setup.
@sentry/react, React Sentry SDK, or Sentry error boundariesIf project is React Router Framework mode using @sentry/react-router, use sentry-react-router-framework-sdk instead of this skill.
> Note: SDK versions and APIs below reflect current Sentry docs at time of writing (@sentry/react ≥8.0.0). > Always verify against docs.sentry.io/platforms/javascript/guides/react/ before implementing.
Run these commands to understand the project before making any recommendations:
bash# Detect React version cat package.json | grep -E '"react"|"react-dom"' # Check for existing Sentry cat package.json | grep '"@sentry/' # Detect router and framework mode hints cat package.json | grep -E '"react-router-dom"|"react-router"|"@react-router/"|"@tanstack/react-router"|"@sentry/react-router"' # Detect state management cat package.json | grep -E '"redux"|"@reduxjs/toolkit"' # Detect build tool ls vite.config.ts vite.config.js webpack.config.js craco.config.js 2>/dev/null cat package.json | grep -E '"vite"|"react-scripts"|"webpack"' # Detect logging libraries cat package.json | grep -E '"pino"|"winston"|"loglevel"' # Check for companion backend in adjacent directories ls ../backend ../server ../api 2>/dev/null cat ../go.mod ../requirements.txt ../Gemfile ../pom.xml 2>/dev/null | head -3
What to determine:
| Question | Impact | |----------|--------| | React 19+? | Use reactErrorHandler() hook pattern | | React <19? | Use Sentry.ErrorBoundary | | @sentry/react already present? | Skip install, go straight to feature config | | React Router Framework mode indicators (@sentry/react-router, @react-router/*)? | Use sentry-react-router-framework-sdk | | react-router-dom v5 / v6 / v7? | Determines which router integration to use | | @tanstack/react-router? | Use tanstackRouterBrowserTracingIntegration() | | Redux in use? | Recommend createReduxEnhancer() | | Vite detected? | Source maps via sentryVitePlugin | | CRA (react-scripts)? | Source maps via @sentry/webpack-plugin in CRACO | | Backend directory found? | Trigger Phase 4 cross-link suggestion |
Present a concrete recommendation based on what you found. Don't ask open-ended questions — lead with a proposal:
Recommended (core coverage):
Optional (enhanced observability):
Sentry.logger.*; recommend when structured log search is neededRecommendation logic:
| Feature | Recommend when... | |---------|------------------| | Error Monitoring | Always — non-negotiable baseline | | Tracing | Always for React SPAs — page load + navigation spans are high-value | | Session Replay | User-facing app, login flows, or checkout pages | | Logging | App needs structured log search or log-to-trace correlation | | Profiling | Performance-critical app; server sends Document-Policy: js-profiling header |
React-specific extras:
reactErrorHandler() on createRootsentry-react-router-framework-sdkcreateReduxEnhancer() to Redux storesentryVitePlugin for source maps (essential for readable stack traces)Propose: "I recommend setting up Error Monitoring + Tracing + Session Replay. Want me to also add Logging or Profiling?"
bashnpm install @sentry/react --save
src/instrument.tsSentry must initialize before any other code runs. Put Sentry.init() in a dedicated sidecar file:
typescriptimport * as Sentry from "@sentry/react"; Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN, // Adjust per build tool (see table below) environment: import.meta.env.MODE, release: import.meta.env.VITE_APP_VERSION, // inject at build time sendDefaultPii: true, integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration({ maskAllText: true, blockAllMedia: true, }), ], // Tracing tracesSampleRate: 1.0, // lower to 0.1–0.2 in production tracePropagationTargets: ["localhost", /^https:\/\/yourapi\.io/], // Session Replay replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, enableLogs: true, });
DSN environment variable by build tool:
| Build Tool | Variable Name | Access in code | |------------|--------------|----------------| | Vite | VITE_SENTRY_DSN | import.meta.env.VITE_SENTRY_DSN | | Create React App | REACT_APP_SENTRY_DSN | process.env.REACT_APP_SENTRY_DSN | | Custom webpack | SENTRY_DSN | process.env.SENTRY_DSN |
Import instrument.ts as the very first import in your entry file:
tsx// src/main.tsx (Vite) or src/index.tsx (CRA/webpack) import "./instrument"; // ← MUST be first import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; createRoot(document.getElementById("root")!).render( <StrictMode> <App /> </StrictMode> );
React 19+ — use reactErrorHandler() on createRoot:
tsximport { reactErrorHandler } from "@sentry/react"; createRoot(document.getElementById("root")!, { onUncaughtError: reactErrorHandler(), onCaughtError: reactErrorHandler(), onRecoverableError: reactErrorHandler(), }).render(<App />);
React <19 — wrap your app in Sentry.ErrorBoundary:
tsximport * as Sentry from "@sentry/react"; createRoot(document.getElementById("root")!).render( <Sentry.ErrorBoundary fallback={<p>Something went wrong</p>} showDialog> <App /> </Sentry.ErrorBoundary> );
Use <Sentry.ErrorBoundary> for any sub-tree that should catch errors independently (route sections, widgets, etc.).
Configure the matching integration for your router (non-framework mode):
| Router | Integration | Notes | |--------|------------|-------| | React Router v7 | reactRouterV7BrowserTracingIntegration | useEffect, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes from react-router | | React Router v6 | reactRouterV6BrowserTracingIntegration | useEffect, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes from react-router-dom | | React Router v5 | reactRouterV5BrowserTracingIntegration | Wrap routes in withSentryRouting(Route) | | TanStack Router | tanstackRouterBrowserTracingIntegration(router) | Pass router instance — no hooks required | | No router / custom | browserTracingIntegration() | Names transactions by URL path |
React Router v6/v7 setup:
typescript// in instrument.ts integrations array: import React from "react"; import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType, } from "react-router-dom"; // or "react-router" for v7 import * as Sentry from "@sentry/react"; import { reactRouterV6BrowserTracingIntegration } from "@sentry/react"; import { createBrowserRouter } from "react-router-dom"; // Option A — createBrowserRouter (recommended for v6.4+): const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter); const router = sentryCreateBrowserRouter([...routes]); // Option B — createBrowserRouter for React Router v7: // const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter); // Option C — integration with hooks (v6 without data APIs): Sentry.init({ integrations: [ reactRouterV6BrowserTracingIntegration({ useEffect: React.useEffect, useLocation, useNavigationType, matchRoutes, createRoutesFromChildren, }), ], });
TanStack Router setup:
typescriptimport { tanstackRouterBrowserTracingIntegration } from "@sentry/react"; // Pass your TanStack router instance: Sentry.init({ integrations: [tanstackRouterBrowserTracingIntegration(router)], });
typescriptimport * as Sentry from "@sentry/react"; import { configureStore } from "@reduxjs/toolkit"; const store = configureStore({ reducer: rootReducer, enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat(Sentry.createReduxEnhancer()), });
Without source maps, stack traces show minified code. Set up the build plugin to upload source maps automatically:
Vite (vite.config.ts):
typescriptimport { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { sentryVitePlugin } from "@sentry/vite-plugin"; export default defineConfig({ build: { sourcemap: "hidden" }, plugins: [ react(), sentryVitePlugin({ org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, }), ], });
Add to .env (never commit):
bashSENTRY_AUTH_TOKEN=sntrys_... SENTRY_ORG=my-org-slug SENTRY_PROJECT=my-project-slug
Create React App (via CRACO):
bashnpm install @craco/craco @sentry/webpack-plugin --save-dev
javascript// craco.config.js const { sentryWebpackPlugin } = require("@sentry/webpack-plugin"); module.exports = { webpack: { plugins: { add: [ sentryWebpackPlugin({ org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, }), ], }, }, };
Walk through features one at a time. Load the reference file, follow its steps, verify before moving on:
| Feature | Reference | Load when... | |---------|-----------|-------------| | Error Monitoring | ${SKILL_ROOT}/references/error-monitoring.md | Always (baseline) | | Tracing | ${SKILL_ROOT}/references/tracing.md | SPA navigation / API call tracing | | Session Replay | ${SKILL_ROOT}/references/session-replay.md | User-facing app | | Logging | ${SKILL_ROOT}/references/logging.md | Structured log search / log-to-trace | | Profiling | ${SKILL_ROOT}/references/profiling.md | Performance-critical app | | React Features | ${SKILL_ROOT}/references/react-features.md | Redux, component tracking, source maps, integrations catalog |
For each feature: Read ${SKILL_ROOT}/references/<feature>.md, follow steps exactly, verify it works.
Sentry.init() Options| Option | Type | Default | Notes | |--------|------|---------|-------| | dsn | string | — | Required. SDK disabled when empty | | environment | string | "production" | e.g., "staging", "development" | | release | string | — | e.g., "my-app@1.0.0" or git SHA — links errors to releases | | sendDefaultPii | boolean | false | Includes IP addresses and request headers | | tracesSampleRate | number | — | 0–1; 1.0 in dev, 0.1–0.2 in prod | | tracesSampler | function | — | Per-transaction sampling; overrides rate | | tracePropagationTargets | (string\|RegExp)[] | — | Outgoing URLs that receive distributed tracing headers | | replaysSessionSampleRate | number | — | Fraction of all sessions recorded | | replaysOnErrorSampleRate | number | — | Fraction of error sessions recorded | | enableLogs | boolean | false | Enable Sentry.logger.* API | | attachStacktrace | boolean | false | Stack traces on captureMessage() calls | | maxBreadcrumbs | number | 100 | Breadcrumbs stored per event | | debug | boolean | false | Verbose SDK output to console | | tunnel | string | — | Proxy URL to bypass ad blockers |
| React Version | Error handling approach | SDK minimum | |---------------|------------------------|-------------| | React 19+ | reactErrorHandler() on createRoot | @sentry/react ≥8.0.0 | | React 16–18 | Sentry.ErrorBoundary component | @sentry/react ≥7.0.0 | | React 16 | componentDidCatch class boundaries | @sentry/react ≥6.0.0 |
Trigger test events to confirm Sentry is receiving data:
tsx// Add a temporary test button anywhere in your app import * as Sentry from "@sentry/react"; function SentryTest() { return ( <> <button onClick={() => { throw new Error("Sentry React test error"); }}> Test Error </button> <button onClick={() => Sentry.captureMessage("Sentry test message", "info")}> Test Message </button> </> ); }
Check the Sentry dashboard:
Set debug: true in Sentry.init() and check the browser console if nothing appears.
After completing React setup, check for a companion backend missing Sentry coverage:
bashls ../backend ../server ../api ../go ../python 2>/dev/null cat ../go.mod 2>/dev/null | head -3 cat ../requirements.txt ../pyproject.toml 2>/dev/null | head -3 cat ../Gemfile 2>/dev/null | head -3 cat ../pom.xml 2>/dev/null | grep '<artifactId>' | head -3
If a backend exists without Sentry configured, suggest the matching skill:
| Backend detected | Suggest skill | |-----------------|--------------| | Go (go.mod) | sentry-go-sdk | | Python (requirements.txt, pyproject.toml) | sentry-python-sdk | | Ruby (Gemfile) | sentry-ruby-sdk | | Java (pom.xml, build.gradle) | Use @sentry/java — see docs.sentry.io/platforms/java/ | | Node.js (Express, Fastify) | Use @sentry/node — see docs.sentry.io/platforms/javascript/guides/express/ |
| Issue | Solution | |-------|----------| | Events not appearing | Set debug: true, check DSN, open browser console for SDK errors | | Source maps not working | Build in production mode (npm run build); verify SENTRY_AUTH_TOKEN is set | | Minified stack traces | Source maps not uploading — check plugin config and auth token | | instrument.ts not running first | Verify it's the first import in entry file before React/app imports | | React 19 errors not captured | Confirm reactErrorHandler() is passed to all three createRoot options | | React <19 errors not captured | Ensure <Sentry.ErrorBoundary> wraps the component tree | | Router transactions named <unknown> | Add router integration matching your router version | | tracePropagationTargets not matching | Check regex escaping; default is localhost and your DSN origin only | | Session replay not recording | Confirm replayIntegration() is in init; check replaysSessionSampleRate | | Redux actions not in breadcrumbs | Add Sentry.createReduxEnhancer() to store enhancers | | Ad blockers dropping events | Set tunnel: "/sentry-tunnel" and add server-side relay endpoint | | High replay storage costs | Lower replaysSessionSampleRate; keep replaysOnErrorSampleRate: 1.0 | | Profiling not working | Verify Document-Policy: js-profiling header is set on document responses |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,743 | 5,341 | -61% | 1 | 1 | 0% | 2,974 | 4,756 | +60% | 0 | 0 | — |
case-02 | fail→fail | 10,885 | 5,847 | -46% | 1 | 1 | 0% | 2,342 | 5,081 | +117% | 0 | 0 | — |
case-03 | fail→fail | 15,825 | 3,806 | -76% | 1 | 1 | 0% | 3,559 | 4,758 | +34% | 0 | 0 | — |
case-04 | pass→fail | 19,078 | 11,697 | -39% | 1 | 1 | 0% | 4,012 | 7,085 | +77% | 0 | 0 | — |
case-05 | fail→fail | 13,273 | 10,494 | -21% | 1 | 1 | 0% | 2,502 | 6,550 | +162% | 0 | 0 | — |
case-06 | pass→pass | 14,477 | 10,915 | -25% | 1 | 1 | 0% | 3,031 | 6,909 | +128% | 0 | 0 | — |
case-07 | fail→pass | 12,139 | 5,743 | -53% | 1 | 1 | 0% | 2,301 | 5,645 | +145% | 0 | 0 | — |
case-08 | pass→pass | 8,566 | 5,612 | -34% | 1 | 1 | 0% | 1,720 | 5,631 | +227% | 0 | 0 | — |
case-09 | pass→pass | 5,905 | 3,023 | -49% | 1 | 1 | 0% | 1,213 | 5,122 | +322% | 0 | 0 | — |
case-10 | pass→pass | 5,931 | 3,695 | -38% | 1 | 1 | 0% | 1,223 | 5,269 | +331% | 0 | 0 | — |
case-11 | pass→pass | 7,160 | 5,438 | -24% | 1 | 1 | 0% | 1,497 | 5,569 | +272% | 0 | 0 | — |
case-12 | pass→pass | 11,911 | 7,728 | -35% | 1 | 1 | 0% | 2,312 | 6,104 | +164% | 0 | 0 | — |
case-13 | fail→pass | 11,415 | 7,767 | -32% | 1 | 1 | 0% | 2,317 | 6,100 | +163% | 0 | 0 | — |
case-14 | pass→pass | 11,519 | 4,575 | -60% | 1 | 1 | 0% | 2,271 | 5,464 | +141% | 0 | 0 | — |
case-15 | fail→pass | 14,189 | 7,810 | -45% | 1 | 1 | 0% | 2,737 | 6,046 | +121% | 0 | 0 | — |
case-16 | pass→pass | 12,886 | 7,734 | -40% | 1 | 1 | 0% | 2,639 | 6,056 | +129% | 0 | 0 | — |
case-17 | pass→pass | 12,412 | 6,978 | -44% | 1 | 1 | 0% | 2,307 | 5,786 | +151% | 0 | 0 | — |
case-18 | pass→pass | 3,437 | 3,360 | -2% | 1 | 1 | 0% | 635 | 5,184 | +716% | 0 | 0 | — |
case-19 | fail→pass | 9,700 | 4,338 | -55% | 1 | 1 | 0% | 1,773 | 5,351 | +202% | 0 | 0 | — |
case-20 | pass→pass | 8,818 | 3,449 | -61% | 1 | 1 | 0% | 1,709 | 5,071 | +197% | 0 | 0 | — |
case-21 | pass→pass | 10,486 | 2,947 | -72% | 1 | 1 | 0% | 1,953 | 5,087 | +160% | 0 | 0 | — |
case-22 | pass→pass | 9,403 | 4,286 | -54% | 1 | 1 | 0% | 1,821 | 5,328 | +193% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +14 percentage points is the difference between those two pass rates over the 20 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.