diff --git a/static/app/gettingStartedDocs/javascript/react.tsx b/static/app/gettingStartedDocs/javascript/react.tsx
deleted file mode 100644
index a77e1a7803471d..00000000000000
--- a/static/app/gettingStartedDocs/javascript/react.tsx
+++ /dev/null
@@ -1,669 +0,0 @@
-import {ExternalLink} from 'sentry/components/core/link';
-import {buildSdkConfig} from 'sentry/components/onboarding/gettingStartedDoc/buildSdkConfig';
-import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout';
-import {widgetCalloutBlock} from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
-import {tracePropagationBlock} from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
-import type {
- ContentBlock,
- Docs,
- DocsParams,
- OnboardingConfig,
-} from 'sentry/components/onboarding/gettingStartedDoc/types';
-import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
-import {
- getAIRulesForCodeEditorStep,
- getUploadSourceMapsStep,
-} from 'sentry/components/onboarding/gettingStartedDoc/utils';
-import {
- getCrashReportJavaScriptInstallSteps,
- getCrashReportModalConfigDescription,
- getCrashReportModalIntroduction,
- getFeedbackConfigOptions,
- getFeedbackConfigureDescription,
-} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
-import {
- getReplayConfigOptions,
- getReplayConfigureDescription,
- getReplayVerifyStep,
-} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
-import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlag';
-import {t, tct} from 'sentry/locale';
-import {
- getJavascriptLogsOnboarding,
- getJavascriptProfilingOnboarding,
-} from 'sentry/utils/gettingStartedDocs/javascript';
-
-type Params = DocsParams;
-
-const getDynamicParts = (params: Params): string[] => {
- const dynamicParts: string[] = [];
-
- if (params.isPerformanceSelected) {
- dynamicParts.push(`
- // Tracing
- tracesSampleRate: 1.0, // Capture 100% of the transactions
- // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
- tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`);
- }
-
- if (params.isReplaySelected) {
- dynamicParts.push(`
- // Session Replay
- replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
- replaysOnErrorSampleRate: 1.0 // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.`);
- }
-
- if (params.isLogsSelected) {
- dynamicParts.push(`
- // Enable logs to be sent to Sentry
- enableLogs: true`);
- }
-
- if (params.isProfilingSelected) {
- dynamicParts.push(`
- // Set profilesSampleRate to 1.0 to profile every transaction.
- // Since profilesSampleRate is relative to tracesSampleRate,
- // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate
- // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would
- // results in 25% of transactions being profiled (0.5*0.5=0.25)
- profilesSampleRate: 1.0`);
- }
-
- return dynamicParts;
-};
-
-const getIntegrations = (params: Params): string[] => {
- const integrations = [];
- if (params.isPerformanceSelected) {
- integrations.push(`Sentry.browserTracingIntegration()`);
- }
-
- if (params.isProfilingSelected) {
- integrations.push(`Sentry.browserProfilingIntegration()`);
- }
-
- if (params.isReplaySelected) {
- integrations.push(
- `Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)})`
- );
- }
-
- if (params.isFeedbackSelected) {
- integrations.push(`
- Sentry.feedbackIntegration({
- colorScheme: "system",
- ${getFeedbackConfigOptions(params.feedbackOptions)}
- }),`);
- }
-
- return integrations;
-};
-
-const getSdkSetupSnippet = (params: Params) => {
- const config = buildSdkConfig({
- params,
- staticParts: [
- `dsn: "${params.dsn.public}"`,
- `// Setting this option to true will send default PII data to Sentry.
- // For example, automatic IP address collection on events
- sendDefaultPii: true`,
- ],
- getIntegrations,
- getDynamicParts,
- });
-
- return `
-import * as Sentry from "@sentry/react";
-
-Sentry.init({
- ${config}
-});
-
-const container = document.getElementById(“app”);
-const root = createRoot(container);
-root.render();
-`;
-};
-
-const getVerifySnippet = (params: Params) => {
- const logsCode = params.isLogsSelected
- ? `
- // Send a log before throwing the error
- Sentry.logger.info('User triggered test error', {
- action: 'test_error_button_click',
- });`
- : '';
-
- return `import * as Sentry from '@sentry/react';
-// Add this button component to your app to test Sentry's error tracking
-function ErrorButton() {
- return (
-
- );
-}`;
-};
-
-const installSnippetBlock: ContentBlock = {
- type: 'code',
- tabs: [
- {
- label: 'npm',
- language: 'bash',
- code: 'npm install --save @sentry/react',
- },
- {
- label: 'yarn',
- language: 'bash',
- code: 'yarn add @sentry/react',
- },
- {
- label: 'pnpm',
- language: 'bash',
- code: 'pnpm add @sentry/react',
- },
- ],
-};
-
-const onboarding: OnboardingConfig = {
- introduction: () =>
- tct(
- "In this quick guide you'll use [strong:npm], [strong:yarn], or [strong:pnpm] to set up:",
- {
- strong: ,
- }
- ),
- install: () => [
- {
- type: StepType.INSTALL,
- content: [
- {
- type: 'text',
- text: tct(
- 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:',
- {code: }
- ),
- },
- installSnippetBlock,
- ],
- },
- ],
- configure: (params: Params) => [
- {
- type: StepType.CONFIGURE,
- content: [
- {
- type: 'text',
- text: t(
- "Initialize Sentry as early as possible in your application's lifecycle."
- ),
- },
- {
- type: 'code',
- tabs: [
- {
- label: 'JavaScript',
- language: 'javascript',
- code: getSdkSetupSnippet(params),
- },
- ],
- },
- {
- type: 'conditional',
- condition: params.isReplaySelected,
- content: [tracePropagationBlock],
- },
- ],
- },
- getUploadSourceMapsStep({
- guideLink: 'https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/',
- ...params,
- }),
- getAIRulesForCodeEditorStep({
- // ATTENTION: The rules defined here must match those in the documentation (see: https://github.com/getsentry/sentry-docs/blob/master/platform-includes/llm-rules-logs/javascript.react.mdx).
- // If you make any changes, please update the docs accordingly.
- rules: `
-These examples should be used as guidance when configuring Sentry functionality within a project.
-
-# Error / Exception Tracking
-
-Use \`Sentry.captureException(error)\` to capture an exception and log the error in Sentry.
-Use this in try catch blocks or areas where exceptions are expected
-
-# Tracing Examples
-
-Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls
-Ensure you are creating custom spans with meaningful names and operations
-Use the \`Sentry.startSpan\` function to create a span
-Child spans can exist within a parent span
-
-## Custom Span instrumentation in component actions
-
-\`\`\`javascript
-function TestComponent() {
- const handleTestButtonClick = () => {
- // Create a transaction/span to measure performance
- Sentry.startSpan(
- {
- op: "ui.click",
- name: "Test Button Click",
- },
- (span) => {
- const value = "some config";
- const metric = "some metric";
-
- // Metrics can be added to the span
- span.setAttribute("config", value);
- span.setAttribute("metric", metric);
-
- doSomething();
- },
- );
- };
-
- return (
-
- );
-}
-\`\`\`
-
-## Custom span instrumentation in API calls
-
-\`\`\`javascript
-async function fetchUserData(userId) {
- return Sentry.startSpan(
- {
- op: "http.client",
- name: \`GET /api/users/\${userId}\`,
- },
- async () => {
- const response = await fetch(\`/api/users/\${userId}\`);
- const data = await response.json();
- return data;
- },
- );
-}
-\`\`\`
-
-# Logs
-
-Where logs are used, ensure Sentry is imported using \`import * as Sentry from "@sentry/react"\`
-Enable logging in Sentry using \`Sentry.init({ enableLogs: true })\`
-Reference the logger using \`const { logger } = Sentry\`
-Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls
-
-## Configuration
-
-### Baseline
-
-\`\`\`javascript
-import * as Sentry from "@sentry/react";
-
-Sentry.init({
- dsn: "${params.dsn.public}",
-
- enableLogs: true,
-});
-\`\`\`
-
-### Logger Integration
-
-\`\`\`javascript
-Sentry.init({
- dsn: "${params.dsn.public}",
- integrations: [
- // send console.log, console.warn, and console.error calls as logs to Sentry
- Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }),
- ],
-});
-\`\`\`
-
-## Logger Examples
-
-\`logger.fmt\` is a template literal function that should be used to bring variables into the structured logs.
-
-\`\`\`javascript
-logger.trace("Starting database connection", { database: "users" });
-logger.debug(logger.fmt\`Cache miss for user: \${userId}\`);
-logger.info("Updated profile", { profileId: 345 });
-logger.warn("Rate limit reached for endpoint", {
- endpoint: "/api/results/",
- isEnterprise: false,
-});
-logger.error("Failed to process payment", {
- orderId: "order_123",
- amount: 99.99,
-});
-logger.fatal("Database connection pool exhausted", {
- database: "users",
- activeConnections: 100,
-});
-\`\`\`
-`,
- }),
- ],
- verify: (params: Params) => [
- {
- type: StepType.VERIFY,
- content: [
- {
- type: 'text',
- text: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
- ),
- },
- {
- type: 'code',
- tabs: [
- {
- label: 'React',
- language: 'javascript',
- code: getVerifySnippet(params),
- },
- ],
- },
- ],
- },
- ],
- nextSteps: (params: Params) => {
- const steps = [
- {
- id: 'react-features',
- name: t('React Features'),
- description: t(
- 'Learn about our first class integration with the React framework.'
- ),
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/features/',
- },
- ];
-
- if (params.isPerformanceSelected) {
- steps.push({
- id: 'react-router',
- name: t('React Router'),
- description: t(
- 'Configure routing, so Sentry can generate parameterized route names for better grouping of tracing data.'
- ),
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/configuration/integrations/react-router/',
- });
- }
-
- if (params.isLogsSelected) {
- steps.push({
- id: 'logs',
- name: t('Logging Integrations'),
- description: t(
- 'Add logging integrations to automatically capture logs from your application.'
- ),
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/logs/#integrations',
- });
- }
-
- return steps;
- },
-};
-
-const replayOnboarding: OnboardingConfig = {
- install: () => [
- {
- type: StepType.INSTALL,
- content: [
- {
- type: 'text',
- text: tct(
- 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]. You need a minimum version 7.27.0 of [code:@sentry/react] in order to use Session Replay. You do not need to install any additional packages.',
- {code: }
- ),
- },
- installSnippetBlock,
- ],
- },
- ],
- configure: (params: Params) => [
- {
- type: StepType.CONFIGURE,
- content: [
- {
- type: 'text',
- text: getReplayConfigureDescription({
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/session-replay/',
- }),
- },
- {
- type: 'code',
- tabs: [
- {
- label: 'JavaScript',
- language: 'javascript',
- code: getSdkSetupSnippet(params),
- },
- ],
- },
- ],
- },
- ],
- verify: getReplayVerifyStep(),
- nextSteps: () => [],
-};
-
-const feedbackOnboarding: OnboardingConfig = {
- install: () => [
- {
- type: StepType.INSTALL,
- content: [
- {
- type: 'text',
- text: tct(
- 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/react]) installed, minimum version 7.85.0.',
- {
- code: ,
- }
- ),
- },
- installSnippetBlock,
- ],
- },
- ],
- configure: (params: Params) => [
- {
- type: StepType.CONFIGURE,
- content: [
- {
- type: 'text',
- text: getFeedbackConfigureDescription({
- linkConfig:
- 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/',
- linkButton:
- 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#bring-your-own-button',
- }),
- },
- {
- type: 'code',
- tabs: [
- {
- label: 'JavaScript',
- language: 'javascript',
- code: getSdkSetupSnippet(params),
- },
- ],
- },
- {
- type: 'custom',
- content: crashReportCallout({
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#crash-report-modal',
- }),
- },
- ],
- },
- ],
- verify: () => [],
- nextSteps: () => [],
-};
-
-const crashReportOnboarding: OnboardingConfig = {
- introduction: () => getCrashReportModalIntroduction(),
- install: (params: Params) => getCrashReportJavaScriptInstallSteps(params),
- configure: () => [
- {
- type: StepType.CONFIGURE,
- content: [
- {
- type: 'text',
- text: getCrashReportModalConfigDescription({
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#crash-report-modal',
- }),
- },
- widgetCalloutBlock({
- link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#user-feedback-widget',
- }),
- ],
- },
- ],
- verify: () => [],
- nextSteps: () => [],
-};
-
-const performanceOnboarding: OnboardingConfig = {
- introduction: () =>
- t(
- "Adding Performance to your React project is simple. Make sure you've got these basics down."
- ),
- install: onboarding.install,
- configure: params => [
- {
- type: StepType.CONFIGURE,
- content: [
- {
- type: 'text',
- text: t(
- "Configuration should happen as early as possible in your application's lifecycle."
- ),
- },
- {
- type: 'code',
- language: 'javascript',
- code: `
-import React from "react";
-import ReactDOM from "react-dom";
-import * as Sentry from "@sentry/react";
-import App from "./App";
-
-Sentry.init({
- dsn: "${params.dsn.public}",
- integrations: [Sentry.browserTracingIntegration()],
-
- // Set tracesSampleRate to 1.0 to capture 100%
- // of transactions for performance monitoring.
- // We recommend adjusting this value in production
- tracesSampleRate: 1.0,
- // Set \`tracePropagationTargets\` to control for which URLs distributed tracing should be enabled
- tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],
-});
-
-ReactDOM.render(, document.getElementById("root"));
-
-// Can also use with React Concurrent Mode
-// ReactDOM.createRoot(document.getElementById('root')).render();
-`,
- },
- {
- type: 'text',
- text: tct(
- 'We recommend adjusting the value of [code:tracesSampleRate] in production. Learn more about tracing [linkTracingOptions:options], how to use the [linkTracesSampler:traces_sampler] function, or how to do [linkSampleTransactions:sampling].',
- {
- code: ,
- linkTracingOptions: (
-
- ),
- linkTracesSampler: (
-
- ),
- linkSampleTransactions: (
-
- ),
- }
- ),
- },
- ],
- },
- {
- title: t('Add Distributed Tracing (Optional)'),
- content: [
- {
- type: 'text',
- text: tct(
- "If you're using the current version of our JavaScript SDK and have enabled the [code: BrowserTracing] integration, distributed tracing will work out of the box. To get around possible [link:Browser CORS] issues, define your [code:tracePropagationTargets].",
- {
- code: ,
- link: (
-
- ),
- }
- ),
- },
- {
- type: 'code',
- language: 'javascript',
- code: `
-Sentry.init({
- dsn: "${params.dsn.public}",
- integrations: [Sentry.browserTracingIntegration()],
- tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]
-});
-`,
- },
- ],
- },
- ],
- verify: () => [
- {
- type: StepType.VERIFY,
- content: [
- {
- type: 'text',
- text: tct(
- 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your React application.',
- {
- link: (
-
- ),
- }
- ),
- },
- ],
- },
- ],
- nextSteps: () => [],
-};
-
-const profilingOnboarding = getJavascriptProfilingOnboarding({
- installSnippetBlock,
- docsLink:
- 'https://docs.sentry.io/platforms/javascript/guides/react/profiling/browser-profiling/',
-});
-
-const logsOnboarding: OnboardingConfig = getJavascriptLogsOnboarding({
- installSnippetBlock,
- docsPlatform: 'react',
- packageName: '@sentry/react',
-});
-
-const docs: Docs = {
- onboarding,
- feedbackOnboardingNpm: feedbackOnboarding,
- replayOnboarding,
- performanceOnboarding,
- crashReportOnboarding,
- profilingOnboarding,
- logsOnboarding,
- featureFlagOnboarding: featureFlag,
-};
-
-export default docs;
diff --git a/static/app/gettingStartedDocs/javascript/react/crashReport.tsx b/static/app/gettingStartedDocs/javascript/react/crashReport.tsx
new file mode 100644
index 00000000000000..69b06d86c1875c
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/crashReport.tsx
@@ -0,0 +1,34 @@
+import {widgetCalloutBlock} from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
+import type {
+ DocsParams,
+ OnboardingConfig,
+} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {
+ getCrashReportJavaScriptInstallSteps,
+ getCrashReportModalConfigDescription,
+ getCrashReportModalIntroduction,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+
+export const crashReport: OnboardingConfig = {
+ introduction: () => getCrashReportModalIntroduction(),
+ install: (params: DocsParams) => getCrashReportJavaScriptInstallSteps(params),
+ configure: () => [
+ {
+ type: StepType.CONFIGURE,
+ content: [
+ {
+ type: 'text',
+ text: getCrashReportModalConfigDescription({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#crash-report-modal',
+ }),
+ },
+ widgetCalloutBlock({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#user-feedback-widget',
+ }),
+ ],
+ },
+ ],
+ verify: () => [],
+ nextSteps: () => [],
+};
diff --git a/static/app/gettingStartedDocs/javascript/react/feedback.tsx b/static/app/gettingStartedDocs/javascript/react/feedback.tsx
new file mode 100644
index 00000000000000..e4e723ab75f43b
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/feedback.tsx
@@ -0,0 +1,64 @@
+import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout';
+import type {
+ DocsParams,
+ OnboardingConfig,
+} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {tct} from 'sentry/locale';
+
+import {getSdkSetupSnippet, installSnippetBlock} from './utils';
+
+export const feedback: OnboardingConfig = {
+ install: () => [
+ {
+ type: StepType.INSTALL,
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/react]) installed, minimum version 7.85.0.',
+ {
+ code: ,
+ }
+ ),
+ },
+ installSnippetBlock,
+ ],
+ },
+ ],
+ configure: (params: DocsParams) => [
+ {
+ type: StepType.CONFIGURE,
+ content: [
+ {
+ type: 'text',
+ text: getFeedbackConfigureDescription({
+ linkConfig:
+ 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/',
+ linkButton:
+ 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#bring-your-own-button',
+ }),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ code: getSdkSetupSnippet(params),
+ },
+ ],
+ },
+ {
+ type: 'custom',
+ content: crashReportCallout({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#crash-report-modal',
+ }),
+ },
+ ],
+ },
+ ],
+ verify: () => [],
+ nextSteps: () => [],
+};
diff --git a/static/app/gettingStartedDocs/javascript/react/index.tsx b/static/app/gettingStartedDocs/javascript/react/index.tsx
new file mode 100644
index 00000000000000..72d5254b184a90
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/index.tsx
@@ -0,0 +1,23 @@
+import type {Docs} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlag';
+import {logs} from 'sentry/gettingStartedDocs/javascript/react/logs';
+
+import {crashReport} from './crashReport';
+import {feedback} from './feedback';
+import {onboarding} from './onboarding';
+import {performance} from './performance';
+import {profiling} from './profiling';
+import {replay} from './replay';
+
+const docs: Docs = {
+ onboarding,
+ feedbackOnboardingNpm: feedback,
+ replayOnboarding: replay,
+ performanceOnboarding: performance,
+ crashReportOnboarding: crashReport,
+ profilingOnboarding: profiling,
+ logsOnboarding: logs,
+ featureFlagOnboarding: featureFlag,
+};
+
+export default docs;
diff --git a/static/app/gettingStartedDocs/javascript/react/logs.tsx b/static/app/gettingStartedDocs/javascript/react/logs.tsx
new file mode 100644
index 00000000000000..2868ca22548df8
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/logs.tsx
@@ -0,0 +1,9 @@
+import {getJavascriptLogsOnboarding} from 'sentry/utils/gettingStartedDocs/javascript';
+
+import {installSnippetBlock} from './utils';
+
+export const logs = getJavascriptLogsOnboarding({
+ installSnippetBlock,
+ docsPlatform: 'react',
+ packageName: '@sentry/react',
+});
diff --git a/static/app/gettingStartedDocs/javascript/react.spec.tsx b/static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx
similarity index 99%
rename from static/app/gettingStartedDocs/javascript/react.spec.tsx
rename to static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx
index 3b1febf24bfad6..e895f82fe22b2b 100644
--- a/static/app/gettingStartedDocs/javascript/react.spec.tsx
+++ b/static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx
@@ -4,7 +4,7 @@ import {textWithMarkupMatcher} from 'sentry-test/utils';
import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
-import docs from './react';
+import docs from '.';
describe('javascript-react onboarding docs', () => {
it('renders onboarding docs correctly', () => {
diff --git a/static/app/gettingStartedDocs/javascript/react/onboarding.tsx b/static/app/gettingStartedDocs/javascript/react/onboarding.tsx
new file mode 100644
index 00000000000000..0b11453ad0b163
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/onboarding.tsx
@@ -0,0 +1,277 @@
+import {tracePropagationBlock} from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
+import type {
+ DocsParams,
+ OnboardingConfig,
+} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {
+ getAIRulesForCodeEditorStep,
+ getUploadSourceMapsStep,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils';
+import {t, tct} from 'sentry/locale';
+
+import {getSdkSetupSnippet, installSnippetBlock} from './utils';
+
+const getVerifySnippet = (params: DocsParams) => {
+ const logsCode = params.isLogsSelected
+ ? `
+ // Send a log before throwing the error
+ Sentry.logger.info('User triggered test error', {
+ action: 'test_error_button_click',
+ });`
+ : '';
+
+ return `import * as Sentry from '@sentry/react';
+// Add this button component to your app to test Sentry's error tracking
+function ErrorButton() {
+ return (
+
+ );
+}`;
+};
+
+export const onboarding: OnboardingConfig = {
+ introduction: () =>
+ tct(
+ "In this quick guide you'll use [strong:npm], [strong:yarn], or [strong:pnpm] to set up:",
+ {
+ strong: ,
+ }
+ ),
+ install: () => [
+ {
+ type: StepType.INSTALL,
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:',
+ {code: }
+ ),
+ },
+ installSnippetBlock,
+ ],
+ },
+ ],
+ configure: (params: DocsParams) => [
+ {
+ type: StepType.CONFIGURE,
+ content: [
+ {
+ type: 'text',
+ text: t(
+ "Initialize Sentry as early as possible in your application's lifecycle."
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ code: getSdkSetupSnippet(params),
+ },
+ ],
+ },
+ {
+ type: 'conditional',
+ condition: params.isReplaySelected,
+ content: [tracePropagationBlock],
+ },
+ ],
+ },
+ getUploadSourceMapsStep({
+ guideLink: 'https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/',
+ ...params,
+ }),
+ getAIRulesForCodeEditorStep({
+ // ATTENTION: The rules defined here must match those in the documentation (see: https://github.com/getsentry/sentry-docs/blob/master/platform-includes/llm-rules-logs/javascript.react.mdx).
+ // If you make any changes, please update the docs accordingly.
+ rules: `
+These examples should be used as guidance when configuring Sentry functionality within a project.
+
+# Error / Exception Tracking
+
+Use \`Sentry.captureException(error)\` to capture an exception and log the error in Sentry.
+Use this in try catch blocks or areas where exceptions are expected
+
+# Tracing Examples
+
+Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls
+Ensure you are creating custom spans with meaningful names and operations
+Use the \`Sentry.startSpan\` function to create a span
+Child spans can exist within a parent span
+
+## Custom Span instrumentation in component actions
+
+\`\`\`javascript
+function TestComponent() {
+ const handleTestButtonClick = () => {
+ // Create a transaction/span to measure performance
+ Sentry.startSpan(
+ {
+ op: "ui.click",
+ name: "Test Button Click",
+ },
+ (span) => {
+ const value = "some config";
+ const metric = "some metric";
+
+ // Metrics can be added to the span
+ span.setAttribute("config", value);
+ span.setAttribute("metric", metric);
+
+ doSomething();
+ },
+ );
+ };
+
+ return (
+
+ );
+}
+\`\`\`
+
+## Custom span instrumentation in API calls
+
+\`\`\`javascript
+async function fetchUserData(userId) {
+ return Sentry.startSpan(
+ {
+ op: "http.client",
+ name: \`GET /api/users/\${userId}\`,
+ },
+ async () => {
+ const response = await fetch(\`/api/users/\${userId}\`);
+ const data = await response.json();
+ return data;
+ },
+ );
+}
+\`\`\`
+
+# Logs
+
+Where logs are used, ensure Sentry is imported using \`import * as Sentry from "@sentry/react"\`
+Enable logging in Sentry using \`Sentry.init({ enableLogs: true })\`
+Reference the logger using \`const { logger } = Sentry\`
+Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls
+
+## Configuration
+
+### Baseline
+
+\`\`\`javascript
+import * as Sentry from "@sentry/react";
+
+Sentry.init({
+ dsn: "${params.dsn.public}",
+
+ enableLogs: true,
+});
+\`\`\`
+
+### Logger Integration
+
+\`\`\`javascript
+Sentry.init({
+ dsn: "${params.dsn.public}",
+ integrations: [
+ // send console.log, console.warn, and console.error calls as logs to Sentry
+ Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }),
+ ],
+});
+\`\`\`
+
+## Logger Examples
+
+\`logger.fmt\` is a template literal function that should be used to bring variables into the structured logs.
+
+\`\`\`javascript
+logger.trace("Starting database connection", { database: "users" });
+logger.debug(logger.fmt\`Cache miss for user: \${userId}\`);
+logger.info("Updated profile", { profileId: 345 });
+logger.warn("Rate limit reached for endpoint", {
+ endpoint: "/api/results/",
+ isEnterprise: false,
+});
+logger.error("Failed to process payment", {
+ orderId: "order_123",
+ amount: 99.99,
+});
+logger.fatal("Database connection pool exhausted", {
+ database: "users",
+ activeConnections: 100,
+});
+\`\`\`
+`,
+ }),
+ ],
+ verify: (params: DocsParams) => [
+ {
+ type: StepType.VERIFY,
+ content: [
+ {
+ type: 'text',
+ text: t(
+ "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'React',
+ language: 'javascript',
+ code: getVerifySnippet(params),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ nextSteps: (params: DocsParams) => {
+ const steps = [
+ {
+ id: 'react-features',
+ name: t('React Features'),
+ description: t(
+ 'Learn about our first class integration with the React framework.'
+ ),
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/features/',
+ },
+ ];
+
+ if (params.isPerformanceSelected) {
+ steps.push({
+ id: 'react-router',
+ name: t('React Router'),
+ description: t(
+ 'Configure routing, so Sentry can generate parameterized route names for better grouping of tracing data.'
+ ),
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/configuration/integrations/react-router/',
+ });
+ }
+
+ if (params.isLogsSelected) {
+ steps.push({
+ id: 'logs',
+ name: t('Logging Integrations'),
+ description: t(
+ 'Add logging integrations to automatically capture logs from your application.'
+ ),
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/logs/#integrations',
+ });
+ }
+
+ return steps;
+ },
+};
diff --git a/static/app/gettingStartedDocs/javascript/react/performance.tsx b/static/app/gettingStartedDocs/javascript/react/performance.tsx
new file mode 100644
index 00000000000000..90a85f76b6bf79
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/performance.tsx
@@ -0,0 +1,119 @@
+import {ExternalLink} from 'sentry/components/core/link';
+import type {OnboardingConfig} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {t, tct} from 'sentry/locale';
+
+import {onboarding} from './onboarding';
+
+export const performance: OnboardingConfig = {
+ introduction: () =>
+ t(
+ "Adding Performance to your React project is simple. Make sure you've got these basics down."
+ ),
+ install: onboarding.install,
+ configure: params => [
+ {
+ type: StepType.CONFIGURE,
+ content: [
+ {
+ type: 'text',
+ text: t(
+ "Configuration should happen as early as possible in your application's lifecycle."
+ ),
+ },
+ {
+ type: 'code',
+ language: 'javascript',
+ code: `
+import React from "react";
+import ReactDOM from "react-dom";
+import * as Sentry from "@sentry/react";
+import App from "./App";
+
+Sentry.init({
+ dsn: "${params.dsn.public}",
+ integrations: [Sentry.browserTracingIntegration()],
+
+ // Set tracesSampleRate to 1.0 to capture 100%
+ // of transactions for performance monitoring.
+ // We recommend adjusting this value in production
+ tracesSampleRate: 1.0,
+ // Set \`tracePropagationTargets\` to control for which URLs distributed tracing should be enabled
+ tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],
+});
+
+ReactDOM.render(, document.getElementById("root"));
+
+// Can also use with React Concurrent Mode
+// ReactDOM.createRoot(document.getElementById('root')).render();
+`,
+ },
+ {
+ type: 'text',
+ text: tct(
+ 'We recommend adjusting the value of [code:tracesSampleRate] in production. Learn more about tracing [linkTracingOptions:options], how to use the [linkTracesSampler:traces_sampler] function, or how to do [linkSampleTransactions:sampling].',
+ {
+ code: ,
+ linkTracingOptions: (
+
+ ),
+ linkTracesSampler: (
+
+ ),
+ linkSampleTransactions: (
+
+ ),
+ }
+ ),
+ },
+ ],
+ },
+ {
+ title: t('Add Distributed Tracing (Optional)'),
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ "If you're using the current version of our JavaScript SDK and have enabled the [code: BrowserTracing] integration, distributed tracing will work out of the box. To get around possible [link:Browser CORS] issues, define your [code:tracePropagationTargets].",
+ {
+ code: ,
+ link: (
+
+ ),
+ }
+ ),
+ },
+ {
+ type: 'code',
+ language: 'javascript',
+ code: `
+Sentry.init({
+ dsn: "${params.dsn.public}",
+ integrations: [Sentry.browserTracingIntegration()],
+ tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]
+});
+`,
+ },
+ ],
+ },
+ ],
+ verify: () => [
+ {
+ type: StepType.VERIFY,
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your React application.',
+ {
+ link: (
+
+ ),
+ }
+ ),
+ },
+ ],
+ },
+ ],
+ nextSteps: () => [],
+};
diff --git a/static/app/gettingStartedDocs/javascript/react/profiling.tsx b/static/app/gettingStartedDocs/javascript/react/profiling.tsx
new file mode 100644
index 00000000000000..eecd58bdf6da9c
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/profiling.tsx
@@ -0,0 +1,9 @@
+import {getJavascriptProfilingOnboarding} from 'sentry/utils/gettingStartedDocs/javascript';
+
+import {installSnippetBlock} from './utils';
+
+export const profiling = getJavascriptProfilingOnboarding({
+ installSnippetBlock,
+ docsLink:
+ 'https://docs.sentry.io/platforms/javascript/guides/react/profiling/browser-profiling/',
+});
diff --git a/static/app/gettingStartedDocs/javascript/react/replay.tsx b/static/app/gettingStartedDocs/javascript/react/replay.tsx
new file mode 100644
index 00000000000000..f731a048a7a924
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/replay.tsx
@@ -0,0 +1,52 @@
+import type {OnboardingConfig} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {
+ getReplayConfigureDescription,
+ getReplayVerifyStep,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
+import {tct} from 'sentry/locale';
+
+import {getSdkSetupSnippet, installSnippetBlock} from './utils';
+
+export const replay: OnboardingConfig = {
+ install: () => [
+ {
+ type: StepType.INSTALL,
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]. You need a minimum version 7.27.0 of [code:@sentry/react] in order to use Session Replay. You do not need to install any additional packages.',
+ {code: }
+ ),
+ },
+ installSnippetBlock,
+ ],
+ },
+ ],
+ configure: params => [
+ {
+ type: StepType.CONFIGURE,
+ content: [
+ {
+ type: 'text',
+ text: getReplayConfigureDescription({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/react/session-replay/',
+ }),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ code: getSdkSetupSnippet(params),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ verify: getReplayVerifyStep(),
+ nextSteps: () => [],
+};
diff --git a/static/app/gettingStartedDocs/javascript/react/utils.tsx b/static/app/gettingStartedDocs/javascript/react/utils.tsx
new file mode 100644
index 00000000000000..e2d82277581259
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react/utils.tsx
@@ -0,0 +1,118 @@
+import {buildSdkConfig} from 'sentry/components/onboarding/gettingStartedDoc/buildSdkConfig';
+import type {
+ ContentBlock,
+ DocsParams,
+} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {getFeedbackConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {getReplayConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
+
+const getDynamicParts = (params: DocsParams): string[] => {
+ const dynamicParts: string[] = [];
+
+ if (params.isPerformanceSelected) {
+ dynamicParts.push(`
+ // Tracing
+ tracesSampleRate: 1.0, // Capture 100% of the transactions
+ // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
+ tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`);
+ }
+
+ if (params.isReplaySelected) {
+ dynamicParts.push(`
+ // Session Replay
+ replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
+ replaysOnErrorSampleRate: 1.0 // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.`);
+ }
+
+ if (params.isLogsSelected) {
+ dynamicParts.push(`
+ // Enable logs to be sent to Sentry
+ enableLogs: true`);
+ }
+
+ if (params.isProfilingSelected) {
+ dynamicParts.push(`
+ // Set profilesSampleRate to 1.0 to profile every transaction.
+ // Since profilesSampleRate is relative to tracesSampleRate,
+ // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate
+ // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would
+ // results in 25% of transactions being profiled (0.5*0.5=0.25)
+ profilesSampleRate: 1.0`);
+ }
+
+ return dynamicParts;
+};
+
+const getIntegrations = (params: DocsParams): string[] => {
+ const integrations = [];
+ if (params.isPerformanceSelected) {
+ integrations.push(`Sentry.browserTracingIntegration()`);
+ }
+
+ if (params.isProfilingSelected) {
+ integrations.push(`Sentry.browserProfilingIntegration()`);
+ }
+
+ if (params.isReplaySelected) {
+ integrations.push(
+ `Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)})`
+ );
+ }
+
+ if (params.isFeedbackSelected) {
+ integrations.push(`
+ Sentry.feedbackIntegration({
+ colorScheme: "system",
+ ${getFeedbackConfigOptions(params.feedbackOptions)}
+ }),`);
+ }
+
+ return integrations;
+};
+
+export function getSdkSetupSnippet(params: DocsParams) {
+ const config = buildSdkConfig({
+ params,
+ staticParts: [
+ `dsn: "${params.dsn.public}"`,
+ `// Setting this option to true will send default PII data to Sentry.
+ // For example, automatic IP address collection on events
+ sendDefaultPii: true`,
+ ],
+ getIntegrations,
+ getDynamicParts,
+ });
+
+ return `
+import * as Sentry from "@sentry/react";
+
+Sentry.init({
+ ${config}
+});
+
+const container = document.getElementById("app");
+const root = createRoot(container);
+root.render();
+`;
+}
+
+export const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/react',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/react',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/react',
+ },
+ ],
+};