diff --git a/static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx b/static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx
index 75039ba63dc957..478d797e70d228 100644
--- a/static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx
+++ b/static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx
@@ -1,8 +1,10 @@
import {Alert} from 'sentry/components/core/alert';
import {ExternalLink} from 'sentry/components/core/link';
import type {
+ ContentBlock,
DocsParams,
OnboardingConfig,
+ OnboardingStep,
} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {t, tct} from 'sentry/locale';
@@ -125,11 +127,13 @@ export const getCrashReportModalIntroduction = () =>
'Collect feedback on your errors by installing our crash-report modal. This allows users to submit feedback after they experience an error via an automatic modal that pops up after an error occurs. The default modal will prompt the user for their name, email address, and description of what occurred.'
);
-export const getCrashReportModalInstallDescriptionJavaScript = () =>
- tct(
+export const getCrashReportModalInstallDescriptionJavaScript = (): ContentBlock => ({
+ type: 'text',
+ text: tct(
'You can collect feedback at the time the event is sent, using [code:beforeSend].',
{code: }
- );
+ ),
+});
export const getCrashReportModalConfigDescription = ({link}: {link: string}) =>
tct(
@@ -137,14 +141,13 @@ export const getCrashReportModalConfigDescription = ({link}: {link: string}) =>
{code:
, link:
}
- ),
- code: [
- {
- label: 'HTML',
- value: 'html',
- language: 'html',
- filename: 'app.component.html',
- code: getVerifySnippetTemplate(),
- },
- ],
- },
- {
- description: tct('Then, in your [code:app.component.ts] add the event handler:', {
- code:
,
- }),
- code: [
- {
- label: 'TypeScript',
- value: 'typescript',
- language: 'typescript',
- filename: 'app.component.ts',
- code: getVerifySnippetComponent(),
- },
- ],
- },
- ],
- };
-}
-
const getInstallConfig = () => [
{
language: 'bash',
@@ -288,6 +245,27 @@ const getInstallConfig = () => [
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/angular',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/angular',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm install @sentry/angular',
+ },
+ ],
+};
+
const onboarding: OnboardingConfig
}
- ),
- configurations: getInstallConfig(),
+ 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,
- configurations: [
+ content: [
{
- description: tct(
+ type: 'text',
+ text: tct(
`Initialize the Sentry Angular SDK in your [code:main.ts] file as early as possible, before initializing Angular:`,
{
code:
,
}
),
- language: 'javascript',
- code: getSdkSetupSnippet(params),
},
{
- description: isModuleConfig(params)
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ code: getSdkSetupSnippet(params),
+ },
+ ],
+ },
+ {
+ type: 'text',
+ text: isModuleConfig(params)
? tct(
"Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.module.ts] file:",
{code:
}
@@ -330,10 +323,18 @@ const onboarding: OnboardingConfig
}
),
- language: 'javascript',
- code: isModuleConfig(params)
- ? getConfigureAppModuleSnippet()
- : getConfigureAppConfigSnippet(),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'JavaScript',
+ language: 'javascript',
+ code: isModuleConfig(params)
+ ? getConfigureAppModuleSnippet()
+ : getConfigureAppConfigSnippet(),
+ },
+ ],
},
],
},
@@ -345,10 +346,52 @@ const onboarding: OnboardingConfig
,
+ }),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'HTML',
+ value: 'html',
+ language: 'html',
+ filename: 'app.component.html',
+ code: '',
+ },
+ ],
+ },
+ {
+ type: 'text',
+ text: tct('Then, in your [code:app.component.ts] add the event handler:', {
+ code:
,
+ }),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'TypeScript',
+ value: 'typescript',
+ language: 'typescript',
+ filename: 'app.component.ts',
+ code: getVerifySnippetComponent(),
+ },
+ ],
+ },
{
- description: t(
+ type: 'text',
+ text: t(
"After clicking the button, you should see the error on Sentry's Issues page."
),
},
diff --git a/static/app/gettingStartedDocs/javascript/astro.tsx b/static/app/gettingStartedDocs/javascript/astro.tsx
index 634d54c175f8da..e0c949335bad41 100644
--- a/static/app/gettingStartedDocs/javascript/astro.tsx
+++ b/static/app/gettingStartedDocs/javascript/astro.tsx
@@ -5,6 +5,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -70,7 +71,7 @@ const getVerifySnippet = () => `
document.querySelector("#error-button").addEventListener("click", handleClick);
`;
-
+// TODO: Remove once the other product areas support content blocks
const getInstallConfig = () => [
{
type: StepType.INSTALL,
@@ -96,6 +97,17 @@ const getInstallConfig = () => [
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'bash',
+ language: 'bash',
+ code: 'npx astro add @sentry/astro',
+ },
+ ],
+};
+
const onboarding: OnboardingConfig = {
introduction: () => (
,
+ }
+ ),
+ },
+ installSnippetBlock,
+ ],
+ },
+ ],
configure: (params: Params) => [
{
type: StepType.CONFIGURE,
- description: tct(
- 'Open up your [astroConfig:astro.config.mjs] file and configure the DSN, and any other settings you need:',
+ content: [
{
- astroConfig:
,
- }
- ),
- configurations: [
+ type: 'text',
+ text: tct(
+ 'Open up your [astroConfig:astro.config.mjs] file and configure the DSN, and any other settings you need:',
+ {
+ astroConfig:
,
+ }
+ ),
+ },
{
- code: [
+ type: 'code',
+ tabs: [
{
label: 'JavaScript',
value: 'javascript',
@@ -136,24 +168,27 @@ const onboarding: OnboardingConfig = {
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'Add your Sentry auth token to the [authTokenEnvVar:SENTRY_AUTH_TOKEN] environment variable:',
{
authTokenEnvVar:
,
}
),
- language: 'bash',
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- value: 'bash',
- language: 'bash',
label: 'bash',
- code: `SENTRY_AUTH_TOKEN=___ORG_AUTH_TOKEN___`,
+ language: 'bash',
+ code: 'SENTRY_AUTH_TOKEN=___ORG_AUTH_TOKEN___',
},
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'You can further customize your SDK by [manualSetupLink:manually initializing the SDK].',
{
manualSetupLink: (
@@ -168,12 +203,16 @@ const onboarding: OnboardingConfig = {
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- 'Then throw a test error anywhere in your app, so you can test that everything is working:'
- ),
- configurations: [
+ content: [
{
- code: [
+ type: 'text',
+ text: t(
+ 'Then throw a test error anywhere in your app, so you can test that everything is working:'
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'Astro',
value: 'html',
diff --git a/static/app/gettingStartedDocs/javascript/ember.tsx b/static/app/gettingStartedDocs/javascript/ember.tsx
index fea25e1c794ea3..a2fc266fd563ff 100644
--- a/static/app/gettingStartedDocs/javascript/ember.tsx
+++ b/static/app/gettingStartedDocs/javascript/ember.tsx
@@ -3,6 +3,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -115,6 +116,7 @@ export default class App extends Application {
`;
};
+// TODO: Remove once the other product areas support content blocks
const getInstallConfig = () => [
{
language: 'bash',
@@ -125,6 +127,17 @@ ember install @sentry/ember
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'ember-cli',
+ language: 'bash',
+ code: 'ember install @sentry/ember',
+ },
+ ],
+};
+
const getVerifyEmberSnippet = () => `
myUndefinedFunction();`;
@@ -139,21 +152,33 @@ const onboarding: OnboardingConfig = {
description: t(
'Sentry captures data by using an SDK within your application’s runtime.'
),
- configurations: getInstallConfig(),
+ content: [
+ {
+ type: 'text',
+ text: t(
+ 'Sentry captures data by using an SDK within your application’s runtime.'
+ ),
+ },
+ installSnippetBlock,
+ ],
},
],
configure: (params: Params) => [
{
type: StepType.CONFIGURE,
- description: tct(
- 'You should [code:init] the Sentry SDK as soon as possible during your application load up in [code:app.js], before initializing Ember:',
+ content: [
{
- code:
,
- }
- ),
- configurations: [
+ type: 'text',
+ text: tct(
+ 'You should [code:init] the Sentry SDK as soon as possible during your application load up in [code:app.js], before initializing Ember:',
+ {
+ code:
,
+ }
+ ),
+ },
{
- code: [
+ type: 'code',
+ tabs: [
{
label: 'JavaScript',
value: 'javascript',
@@ -172,9 +197,26 @@ const onboarding: OnboardingConfig = {
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
- ),
+ 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: 'JavaScript',
+ value: 'javascript',
+ language: 'javascript',
+ code: getVerifyEmberSnippet(),
+ },
+ ],
+ },
+ ],
+
configurations: [
{
code: [
diff --git a/static/app/gettingStartedDocs/javascript/gatsby.tsx b/static/app/gettingStartedDocs/javascript/gatsby.tsx
index 36d23d60d3f2c4..91844dd3bbf91d 100644
--- a/static/app/gettingStartedDocs/javascript/gatsby.tsx
+++ b/static/app/gettingStartedDocs/javascript/gatsby.tsx
@@ -5,9 +5,11 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
+ OnboardingStep,
} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
@@ -116,19 +118,22 @@ root.render(
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'JavaScript',
- value: 'javascript',
language: 'javascript',
code: `module.exports = {
plugins: [{
@@ -139,14 +144,17 @@ const getConfigureStep = (params: Params) => {
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'Then, configure your [codeSentry:Sentry.init:]. For this, create a new file called [codeSentry:sentry.config.js] in the root of your project and add the following code:',
{codeSentry:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'JavaScript',
- value: 'javascript',
language: 'javascript',
filename: 'sentry.config.js',
code: getSdkSetupSnippet(params),
@@ -157,6 +165,7 @@ const getConfigureStep = (params: Params) => {
};
};
+// TODO: Remove once the other product areas support content blocks
const getInstallConfig = () => [
{
language: 'bash',
@@ -173,6 +182,27 @@ const getInstallConfig = () => [
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/gatsby',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/gatsby',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/gatsby',
+ },
+ ],
+};
+
const onboarding: OnboardingConfig = {
introduction: () =>
tct(
@@ -184,11 +214,16 @@ const onboarding: OnboardingConfig = {
install: () => [
{
type: StepType.INSTALL,
- description: tct(
- 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:',
- {code:
}
- ),
- configurations: getInstallConfig(),
+ 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) => [
@@ -201,12 +236,16 @@ const onboarding: OnboardingConfig = {
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
- ),
- configurations: [
+ content: [
{
- code: [
+ 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: 'JavaScript',
value: 'javascript',
diff --git a/static/app/gettingStartedDocs/javascript/javascript.tsx b/static/app/gettingStartedDocs/javascript/javascript.tsx
index 078862de86685f..88fa11f676df44 100644
--- a/static/app/gettingStartedDocs/javascript/javascript.tsx
+++ b/static/app/gettingStartedDocs/javascript/javascript.tsx
@@ -8,6 +8,7 @@ import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedba
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
BasePlatformOptions,
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -274,6 +275,7 @@ Sentry.init({
const getVerifyJSSnippet = () => `
myUndefinedFunction();`;
+// TODO: Remove once the other product areas support content blocks
const getInstallConfig = () => [
{
language: 'bash',
@@ -300,27 +302,53 @@ const getInstallConfig = () => [
},
];
-const getVerifyConfig = () => [
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/browser',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/browser',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/browser',
+ },
+ ],
+};
+
+const verifySnippetBlock: ContentBlock[] = [
{
- type: StepType.VERIFY,
- description: t(
+ 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."
),
- configurations: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- code: [
- {
- label: 'Javascript',
- value: 'javascript',
- language: 'javascript',
- code: getVerifyJSSnippet(),
- },
- ],
+ label: 'Javascript',
+ language: 'javascript',
+ code: getVerifyJSSnippet(),
},
],
},
];
+const getVerifyConfig = () => [
+ {
+ type: StepType.VERIFY,
+ content: verifySnippetBlock,
+ },
+];
+
const getAiRulesConfig = (params: Params) =>
getAIRulesForCodeEditorStep({
rules: `
@@ -638,24 +666,32 @@ const packageManagerOnboarding: OnboardingConfig- {tctCode( - 'Build and run your application and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.' - )} -
-{t('Or, throw an error in a simple vue component.')}
-
,
- }
- ),
- configurations: getInstallConfig(params),
+ 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/nuxt]) installed, minimum version 7.85.0.',
+ {
+ code:
,
+ }
+ ),
+ },
+ ...getInstallContent(params),
+ ],
},
],
configure: (params: Params) => [
@@ -217,17 +225,25 @@ const feedbackOnboarding: OnboardingConfig = {
};
const crashReportOnboarding: OnboardingConfig = {
- introduction: () => getCrashReportModalIntroduction(),
- install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
+ introduction: getCrashReportModalIntroduction,
+ install: getCrashReportJavaScriptInstallStep,
configure: () => [
{
type: StepType.CONFIGURE,
- description: getCrashReportModalConfigDescription({
- link: 'https://docs.sentry.io/platforms/javascript/guides/nuxt/user-feedback/configuration/#crash-report-modal',
- }),
- additionalInfo: widgetCallout({
- link: 'https://docs.sentry.io/platforms/javascript/guides/nuxt/user-feedback/#user-feedback-widget',
- }),
+ content: [
+ {
+ type: 'text',
+ text: getCrashReportModalConfigDescription({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/nuxt/user-feedback/configuration/#crash-report-modal',
+ }),
+ },
+ {
+ type: 'text',
+ text: widgetCallout({
+ link: 'https://docs.sentry.io/platforms/javascript/guides/nuxt/user-feedback/#user-feedback-widget',
+ }),
+ },
+ ],
},
],
verify: () => [],
diff --git a/static/app/gettingStartedDocs/javascript/react-router.spec.tsx b/static/app/gettingStartedDocs/javascript/react-router.spec.tsx
new file mode 100644
index 00000000000000..0d6e4e0029457b
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/react-router.spec.tsx
@@ -0,0 +1,168 @@
+import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
+import {screen} from 'sentry-test/reactTestingLibrary';
+import {textWithMarkupMatcher} from 'sentry-test/utils';
+
+import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
+
+import docs from './react-router';
+
+describe('javascript-react-router onboarding docs', function () {
+ it('renders onboarding docs correctly', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Renders main headings
+ expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Configure'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument();
+
+ // Includes import statement
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/import \* as Sentry from "@sentry\/react-router"/)
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('displays sample rates by default', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ProductSolution.SESSION_REPLAY,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/tracesSampleRate/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables performance setting the tracesSampleRate to 1', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/reactRouterTracingIntegration\(\)/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables replay by setting replay samplerates', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.SESSION_REPLAY,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replayIntegration\(/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables profiling when selected', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/nodeProfilingIntegration\(\)/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes React Router specific configurations', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Check for React Router specific setup
+ expect(
+ screen.getByText(textWithMarkupMatcher(/createSentryHandleRequest/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/handleError: HandleErrorFunction/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/captureException\(error\)/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes server and client setup', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Check for client setup
+ expect(
+ screen.getByText(textWithMarkupMatcher(/entry\.client\.tsx/))
+ ).toBeInTheDocument();
+ expect(screen.getByText(textWithMarkupMatcher(/HydratedRouter/))).toBeInTheDocument();
+
+ // Check for server setup
+ expect(
+ screen.getByText(textWithMarkupMatcher(/instrument\.server\.mjs/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/entry\.server\.tsx/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/react-router reveal/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes error boundary setup', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(screen.getByText(textWithMarkupMatcher(/ErrorBoundary/))).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Route\.ErrorBoundaryProps/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/isRouteErrorResponse/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes package.json scripts update', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/NODE_OPTIONS.*--import.*instrument\.server\.mjs/)
+ )
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/react-router dev/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/react-router-serve/))
+ ).toBeInTheDocument();
+ });
+
+ it('displays test error route in verify section', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Sentry Test Error/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/This page will throw an error!/))
+ ).toBeInTheDocument();
+ });
+});
diff --git a/static/app/gettingStartedDocs/javascript/remix.tsx b/static/app/gettingStartedDocs/javascript/remix.tsx
index ceec1afe78ccf6..8f2d036c02dd8f 100644
--- a/static/app/gettingStartedDocs/javascript/remix.tsx
+++ b/static/app/gettingStartedDocs/javascript/remix.tsx
@@ -49,6 +49,7 @@ const getConfigStep = ({isSelfHosted, organization, projectSlug}: Params) => {
];
};
+// TODO: Refactor once the other product areas support content blocks
const getInstallConfig = (params: Params) => [
{
type: StepType.INSTALL,
@@ -77,17 +78,21 @@ const onboarding: OnboardingConfig = {
{
collapsible: true,
title: t('Manual Configuration'),
- description: tct(
- 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].',
+ content: [
{
- manualSetupLink: (
-
- {tct(
- 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.',
- {
- code: ,
- }
- )}
-
- {t( - 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.' - )} -
-
,
+ }
+ ),
+ },
+ {
+ type: 'text',
+ text: t(
+ 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.'
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'Javascript',
value: 'javascript',
diff --git a/static/app/gettingStartedDocs/javascript/solid.tsx b/static/app/gettingStartedDocs/javascript/solid.tsx
index 4e6285607e6e79..62888db8006af1 100644
--- a/static/app/gettingStartedDocs/javascript/solid.tsx
+++ b/static/app/gettingStartedDocs/javascript/solid.tsx
@@ -5,6 +5,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -134,6 +135,7 @@ const getVerifySnippet = () => `
Throw error
`;
+// TODO: Remove once the other product areas support content blocks
const getInstallConfig = () => [
{
language: 'bash',
@@ -160,6 +162,27 @@ const getInstallConfig = () => [
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/solid',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/solid',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/solid',
+ },
+ ],
+};
+
const onboarding: OnboardingConfig = {
introduction: () => (
,
- }
- ),
- configurations: getInstallConfig(),
+ 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,
- description: tct(
- "Initialize Sentry as early as possible in your application's lifecycle, usually your solid app's entry point ([code:main.ts/js]):",
- {code:
}
- ),
- configurations: [
+ content: [
{
- code: [
+ type: 'text',
+ text: tct(
+ "Initialize Sentry as early as possible in your application's lifecycle, usually your solid app's entry point ([code:main.ts/js]):",
+ {code:
}
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'JavaScript',
value: 'javascript',
@@ -214,12 +246,16 @@ const onboarding: OnboardingConfig = {
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
- ),
- configurations: [
+ content: [
{
- code: [
+ 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: 'JavaScript',
value: 'javascript',
diff --git a/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx b/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx
new file mode 100644
index 00000000000000..c78cfc56af0a98
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx
@@ -0,0 +1,116 @@
+import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
+import {screen} from 'sentry-test/reactTestingLibrary';
+import {textWithMarkupMatcher} from 'sentry-test/utils';
+
+import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
+
+import docs from './solidstart';
+
+describe('javascript-solidstart onboarding docs', function () {
+ it('renders onboarding docs correctly', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Renders main headings
+ expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Configure'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument();
+
+ // Includes import statement
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/import \* as Sentry from "@sentry\/solidstart"/)
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('enables performance setting the tracesSampleRate to 1', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/solidRouterBrowserTracingIntegration\(\)/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables replay by setting replay samplerates', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.SESSION_REPLAY,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replayIntegration\(/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables profiling by setting profiling sample rates', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Sentry.browserProfilingIntegration\(\)/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes SolidStart specific configurations', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ],
+ });
+
+ // Check for SolidStart specific setup
+ expect(
+ screen.getByText(textWithMarkupMatcher(/sentryBeforeResponseMiddleware/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/withSentryRouterRouting/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/instrument\.server\.mjs/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes server instrumentation setup', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/NODE_OPTIONS.*--import.*instrument\.server\.mjs/)
+ )
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/entry-client\.tsx/))
+ ).toBeInTheDocument();
+ expect(screen.getByText(textWithMarkupMatcher(/middleware\.ts/))).toBeInTheDocument();
+ });
+
+ it('displays test error button in verify section', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(screen.getByText(textWithMarkupMatcher(/Throw error/))).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Sentry Frontend Error/))
+ ).toBeInTheDocument();
+ });
+});
diff --git a/static/app/gettingStartedDocs/javascript/svelte.tsx b/static/app/gettingStartedDocs/javascript/svelte.tsx
index 865624ecff3c88..7d087c49620a29 100644
--- a/static/app/gettingStartedDocs/javascript/svelte.tsx
+++ b/static/app/gettingStartedDocs/javascript/svelte.tsx
@@ -3,6 +3,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -154,6 +155,27 @@ const getInstallConfig = () => [
},
];
+const installSnippetBlock: ContentBlock = {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/svelte',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/svelte',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/svelte',
+ },
+ ],
+};
+
const onboarding: OnboardingConfig = {
introduction: () =>
tct(
@@ -165,23 +187,32 @@ const onboarding: OnboardingConfig = {
install: () => [
{
type: StepType.INSTALL,
- description: tct(
- 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:',
- {code:
}
- ),
- configurations: getInstallConfig(),
+ 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,
- description: tct(
- "Initialize Sentry as early as possible in your application's lifecycle, usually your Svelte app's entry point ([code:main.ts/js]):",
- {code:
}
- ),
- configurations: [
+ content: [
{
- code: [
+ type: 'text',
+ text: tct(
+ "Initialize Sentry as early as possible in your application's lifecycle, usually your Svelte app's entry point ([code:main.ts/js]):",
+ {code:
}
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'Svelte v5',
value: 'svelte v5',
@@ -206,12 +237,16 @@ const onboarding: OnboardingConfig = {
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
- ),
- configurations: [
+ content: [
{
- code: [
+ 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: 'Svelte v5',
value: 'svelte v5',
diff --git a/static/app/gettingStartedDocs/javascript/sveltekit.tsx b/static/app/gettingStartedDocs/javascript/sveltekit.tsx
index f8e4481662a43e..45bb3ed94723a7 100644
--- a/static/app/gettingStartedDocs/javascript/sveltekit.tsx
+++ b/static/app/gettingStartedDocs/javascript/sveltekit.tsx
@@ -6,6 +6,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f
import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
import type {
+ ContentBlock,
Docs,
DocsParams,
OnboardingConfig,
@@ -30,13 +31,17 @@ import {getNodeAgentMonitoringOnboarding} from 'sentry/utils/gettingStartedDocs/
type Params = DocsParams;
-const getConfigStep = ({isSelfHosted, organization, projectSlug}: Params) => {
+const getConfigStep = ({
+ isSelfHosted,
+ organization,
+ projectSlug,
+}: Params): ContentBlock[] => {
const urlParam = isSelfHosted ? '' : '--saas';
return [
{
- type: StepType.INSTALL,
- description: tct(
+ type: 'text',
+ text: tct(
'Configure your app automatically by running the [wizardLink:Sentry wizard] in the root of your project.',
{
wizardLink: (
@@ -44,45 +49,41 @@ const getConfigStep = ({isSelfHosted, organization, projectSlug}: Params) => {
),
}
),
- configurations: [
- {
- language: 'bash',
- code: `npx @sentry/wizard@latest -i sveltekit ${urlParam} --org ${organization.slug} --project ${projectSlug}`,
- },
- ],
+ },
+ {
+ type: 'code',
+ language: 'bash',
+ code: `npx @sentry/wizard@latest -i sveltekit ${urlParam} --org ${organization.slug} --project ${projectSlug}`,
},
];
};
-const getInstallConfig = (params: Params) => [
- {
- type: StepType.INSTALL,
- configurations: getConfigStep(params),
- },
-];
-
const onboarding: OnboardingConfig = {
install: (params: Params) => [
{
title: t('Automatic Configuration (Recommended)'),
- configurations: getConfigStep(params),
+ content: getConfigStep(params),
},
],
configure: params => [
{
collapsible: true,
title: t('Manual Configuration'),
- description: tct(
- 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].',
+ content: [
{
- manualSetupLink: (
-
- {tct(
- 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.',
- {
- code: ,
- }
- )}
-
- {t( - 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.' - )} -
-
+ {tct(
+ 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.',
+ {
+ code: ,
+ }
+ )}
+
+ {t( + 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.' + )} +
+
,
- }
- ),
- configurations: getInstallConfig(params),
+ 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/sveltekit]) installed, minimum version 7.85.0.',
+ {
+ code:
,
+ }
+ ),
+ },
+ ...getConfigStep(params),
+ ],
},
],
configure: (params: Params) => [
diff --git a/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx b/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx
new file mode 100644
index 00000000000000..93aef3025adb24
--- /dev/null
+++ b/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx
@@ -0,0 +1,127 @@
+import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
+import {screen} from 'sentry-test/reactTestingLibrary';
+import {textWithMarkupMatcher} from 'sentry-test/utils';
+
+import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types';
+
+import docs from './tanstackstart-react';
+
+describe('javascript-tanstackstart-react onboarding docs', function () {
+ it('renders onboarding docs correctly', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Renders main headings
+ expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Set up the SDK'})).toBeInTheDocument();
+ expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument();
+
+ // Includes import statement
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/import \* as Sentry from "@sentry\/tanstackstart-react"/)
+ )
+ ).toBeInTheDocument();
+
+ // Includes configuration wrapper
+ expect(
+ screen.getByText(textWithMarkupMatcher(/wrapVinxiConfigWithSentry/))
+ ).toBeInTheDocument();
+ });
+
+ it('displays sample rates by default', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ProductSolution.SESSION_REPLAY,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/tracesSampleRate/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/))
+ ).toBeInTheDocument();
+ });
+
+ it('enables performance setting the tracesSampleRate to 1', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.PERFORMANCE_MONITORING,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ textWithMarkupMatcher(/tanstackRouterBrowserTracingIntegration\(router\)/)
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('enables replay by setting replay samplerates', () => {
+ renderWithOnboardingLayout(docs, {
+ selectedProducts: [
+ ProductSolution.ERROR_MONITORING,
+ ProductSolution.SESSION_REPLAY,
+ ],
+ });
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/replayIntegration\(\)/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes TanStack Start specific configurations', () => {
+ renderWithOnboardingLayout(docs);
+
+ // Check for TanStack Start specific setup
+ expect(
+ screen.getByText(textWithMarkupMatcher(/wrapCreateRootRouteWithSentry/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/wrapStreamHandlerWithSentry/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/sentryGlobalServerMiddlewareHandler/))
+ ).toBeInTheDocument();
+ });
+
+ it('includes error boundary configuration', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/withErrorBoundary/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/captureException/))
+ ).toBeInTheDocument();
+ });
+
+ it('displays test error button in verify section', () => {
+ renderWithOnboardingLayout(docs);
+
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Break the world/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/Sentry Test Error/))
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(textWithMarkupMatcher(/sentry-example-api/))
+ ).toBeInTheDocument();
+ });
+});
diff --git a/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx b/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx
index 799c76a9365d46..996b73b6331635 100644
--- a/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx
+++ b/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx
@@ -12,61 +12,65 @@ import {getNodeAgentMonitoringOnboarding} from 'sentry/utils/gettingStartedDocs/
type Params = DocsParams;
-const getInstallConfig = () => [
- {
- language: 'bash',
- code: [
- {
- label: 'npm',
- value: 'npm',
- language: 'bash',
- code: 'npm install --save @sentry/tanstackstart-react',
- },
- {
- label: 'yarn',
- value: 'yarn',
- language: 'bash',
- code: 'yarn add @sentry/tanstackstart-react',
- },
- {
- label: 'pnpm',
- value: 'pnpm',
- language: 'bash',
- code: 'pnpm add @sentry/tanstackstart-react',
- },
- ],
- },
-];
-
const onboarding: OnboardingConfig = {
introduction: () =>
t("In this guide you'll set up the Sentry TanStack Start React SDK"),
install: () => [
{
type: StepType.INSTALL,
- description: tct(
- 'Add the Sentry TanStack Start SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:',
- {code:
}
- ),
- configurations: getInstallConfig(),
+ content: [
+ {
+ type: 'text',
+ text: tct(
+ 'Add the Sentry TanStack Start SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:',
+ {code:
}
+ ),
+ },
+ {
+ type: 'code',
+ tabs: [
+ {
+ label: 'npm',
+ language: 'bash',
+ code: 'npm install --save @sentry/tanstackstart-react',
+ },
+ {
+ label: 'yarn',
+ language: 'bash',
+ code: 'yarn add @sentry/tanstackstart-react',
+ },
+ {
+ label: 'pnpm',
+ language: 'bash',
+ code: 'pnpm add @sentry/tanstackstart-react',
+ },
+ ],
+ },
+ ],
},
],
configure: (params: Params) => [
{
title: t('Set up the SDK'),
- description: t(
- 'In the following steps we will set up the SDK and instrument various parts of your application.'
- ),
- configurations: [
+ content: [
{
- description: tct(
+ type: 'text',
+ text: t(
+ 'In the following steps we will set up the SDK and instrument various parts of your application.'
+ ),
+ },
+ {
+ type: 'text',
+ text: tct(
"First, extend your app's default TanStack Start configuration by adding [code:wrapVinxiConfigWithSentry] to your [code:app.config.ts] file:",
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'TypeScript',
- value: 'typescript',
language: 'typescript',
filename: 'app.config.ts',
code: `import { defineConfig } from "@tanstack/react-start/config";
@@ -90,11 +94,15 @@ export default wrapVinxiConfigWithSentry(config, {
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'In future versions of this SDK, setting the [code:SENTRY_AUTH_TOKEN] environment variable during your build will upload sourcemaps for you to see unminified errors in Sentry.',
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
value: 'bash',
language: 'bash',
@@ -104,13 +112,17 @@ export default wrapVinxiConfigWithSentry(config, {
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'Add the following initialization code to your [code:app/client.tsx] file to initialize Sentry on the client:',
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
filename: 'app/client.tsx',
@@ -168,13 +180,17 @@ hydrateRoot(document,
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
filename: 'app/ssr.tsx',
@@ -202,13 +218,17 @@ Sentry.init({
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
"Wrap TanStack Start's [code:createRootRoute] function using [code:wrapCreateRootRouteWithSentry] to apply tracing to Server-Side Rendering (SSR):",
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
filename: 'app/routes/__root.tsx',
@@ -223,13 +243,17 @@ export const Route = wrapCreateRootRouteWithSentry(createRootRoute)({
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
"Wrap TanStack Start's [code:defaultStreamHandler] function using [code:wrapStreamHandlerWithSentry] to instrument requests to your server:",
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
filename: 'app/ssr.tsx',
@@ -249,11 +273,15 @@ export default createStartHandler({
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
"Add the [code:sentryGlobalServerMiddlewareHandler] to your global middlewares to instrument your server function invocations. If you haven't done so, create a [code:app/global-middleware.ts] file.",
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'TypeScript',
value: 'typescript',
@@ -274,18 +302,23 @@ registerGlobalMiddleware({
],
},
{
- description: t(
+ type: 'text',
+ text: t(
'The Sentry SDK cannot capture errors that you manually caught yourself with error boundaries.'
),
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'If you have React error boundaries in your app and you want to report errors that these boundaries catch to Sentry, apply the [code:withErrorBoundary] wrapper to your [code:ErrorBoundary]:',
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
code: `import React from "react";
@@ -305,13 +338,17 @@ export const MySentryWrappedErrorBoundary = withErrorBoundary(
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
'If you defined [code:errorComponents] in your Code-Based TanStack Router routes, capture the [code:error] argument with [code:captureException] inside a [code:useEffect] hook:',
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
+ label: 'TypeScript',
value: 'tsx',
language: 'tsx',
code: `import { createRoute } from "@tanstack/react-router";
@@ -337,18 +374,24 @@ const route = createRoute({
verify: () => [
{
type: StepType.VERIFY,
- description: t(
- "Let's test your setup and confirm that Sentry is working correctly and sending data to your Sentry project."
- ),
- configurations: [
+ content: [
{
- description: t(
+ type: 'text',
+ text: t(
+ "Let's test your setup and confirm that Sentry is working correctly and sending data to your Sentry project."
+ ),
+ },
+ {
+ type: 'text',
+ text: t(
'To verify that Sentry captures errors and creates issues in your Sentry project, add a test button to an existing page or create a new one:'
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
- value: 'tsx',
+ label: 'TypeScript',
language: 'tsx',
code: `}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
label: 'TypeScript',
- value: 'typescript',
language: 'typescript',
filename: 'app/routes/api/sentry-example-api.ts',
code: `import { json } from "@tanstack/react-start";
@@ -387,14 +433,17 @@ export const APIRoute = createAPIFileRoute("/api/sentry-example-api")({
],
},
{
- description: tct(
+ type: 'text',
+ text: tct(
"Next, update your test button to call this route and throw an error if the response isn't [code:ok]:",
{code:
}
),
- code: [
+ },
+ {
+ type: 'code',
+ tabs: [
{
- label: 'tsx',
- value: 'tsx',
+ label: 'TypeScript',
language: 'tsx',
code: `