nextjs.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import List from 'sentry/components/list/';
  5. import ListItem from 'sentry/components/list/listItem';
  6. import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout';
  7. import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
  8. import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
  9. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  10. import type {
  11. Docs,
  12. DocsParams,
  13. OnboardingConfig,
  14. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  15. import {
  16. getCrashReportJavaScriptInstallStep,
  17. getCrashReportModalConfigDescription,
  18. getCrashReportModalIntroduction,
  19. getFeedbackConfigureDescription,
  20. getFeedbackSDKSetupSnippet,
  21. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  22. import {getJSMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
  23. import {
  24. getReplayConfigureDescription,
  25. getReplaySDKSetupSnippet,
  26. getReplayVerifyStep,
  27. } from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
  28. import TextCopyInput from 'sentry/components/textCopyInput';
  29. import {t, tct} from 'sentry/locale';
  30. import {space} from 'sentry/styles/space';
  31. import {trackAnalytics} from 'sentry/utils/analytics';
  32. type Params = DocsParams;
  33. const getInstallSnippet = ({isSelfHosted, organization, projectSlug}: Params) => {
  34. const urlParam = isSelfHosted ? '' : '--saas';
  35. return `npx @sentry/wizard@latest -i nextjs ${urlParam} --org ${organization.slug} --project ${projectSlug}`;
  36. };
  37. const getInstallConfig = (params: Params) => {
  38. return [
  39. {
  40. description: tct(
  41. 'Configure your app automatically by running the [wizardLink:Sentry wizard] in the root of your project.',
  42. {
  43. wizardLink: (
  44. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/#install" />
  45. ),
  46. }
  47. ),
  48. language: 'bash',
  49. code: getInstallSnippet(params),
  50. },
  51. ];
  52. };
  53. const getManualInstallConfig = () => [
  54. {
  55. language: 'bash',
  56. code: [
  57. {
  58. label: 'npm',
  59. value: 'npm',
  60. language: 'bash',
  61. code: 'npm install --save @sentry/nextjs',
  62. },
  63. {
  64. label: 'yarn',
  65. value: 'yarn',
  66. language: 'bash',
  67. code: 'yarn add @sentry/nextjs',
  68. },
  69. ],
  70. },
  71. ];
  72. const onboarding: OnboardingConfig = {
  73. install: (params: Params) => [
  74. {
  75. title: t('Automatic Configuration (Recommended)'),
  76. configurations: getInstallConfig(params),
  77. },
  78. ],
  79. configure: () => [
  80. {
  81. title: t('Manual Configuration'),
  82. collapsible: true,
  83. configurations: [
  84. {
  85. description: (
  86. <Fragment>
  87. <p>
  88. {tct(
  89. 'Alternatively, you can also [manualSetupLink:set up the SDK manually], by following these steps:',
  90. {
  91. manualSetupLink: (
  92. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/" />
  93. ),
  94. }
  95. )}
  96. </p>
  97. <List symbol="bullet">
  98. <ListItem>
  99. {tct(
  100. 'Create [code:sentry.server.config.js], [code:sentry.client.config.js] and [code:sentry.edge.config.js] with the default [code:Sentry.init].',
  101. {
  102. code: <code />,
  103. }
  104. )}
  105. </ListItem>
  106. <ListItem>
  107. {tct(
  108. 'Create or update the Next.js instrumentation file [instrumentationCode:instrumentation.ts] to initialize the SDK with the configuration files added in the previous step.',
  109. {
  110. instrumentationCode: <code />,
  111. }
  112. )}
  113. </ListItem>
  114. <ListItem>
  115. {tct(
  116. 'Create or update your Next.js config [nextConfig:next.config.js] with the default Sentry configuration.',
  117. {
  118. nextConfig: <code />,
  119. }
  120. )}
  121. </ListItem>
  122. <ListItem>
  123. {tct(
  124. 'Create a [bundlerPluginsEnv:.env.sentry-build-plugin] with an auth token (which is used to upload source maps when building the application).',
  125. {
  126. bundlerPluginsEnv: <code />,
  127. }
  128. )}
  129. </ListItem>
  130. <ListItem>
  131. {t('Add an example page to your app to verify your Sentry setup.')}
  132. </ListItem>
  133. </List>
  134. </Fragment>
  135. ),
  136. },
  137. ],
  138. },
  139. ],
  140. verify: (params: Params) => [
  141. {
  142. type: StepType.VERIFY,
  143. description: (
  144. <Fragment>
  145. <p>
  146. {tct(
  147. 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.',
  148. {
  149. code: <code />,
  150. }
  151. )}
  152. </p>
  153. <p>
  154. {t(
  155. 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.'
  156. )}
  157. </p>
  158. </Fragment>
  159. ),
  160. configurations: [
  161. {
  162. code: [
  163. {
  164. label: 'Javascript',
  165. value: 'javascript',
  166. language: 'javascript',
  167. code: `myUndefinedFunction();`,
  168. },
  169. ],
  170. },
  171. ],
  172. additionalInfo: (
  173. <Fragment>
  174. <p>
  175. {t(
  176. 'If you see an issue in your Sentry dashboard, you have successfully set up Sentry with Next.js.'
  177. )}
  178. </p>
  179. <Divider />
  180. <DSNText>
  181. <p>
  182. {tct(
  183. "If you already have the configuration for Sentry in your application, and just need this project's ([projectSlug]) DSN, you can find it below:",
  184. {
  185. projectSlug: <code>{params.projectSlug}</code>,
  186. }
  187. )}
  188. </p>
  189. </DSNText>
  190. {params.organization && (
  191. <TextCopyInput
  192. onCopy={() =>
  193. trackAnalytics('onboarding.nextjs-dsn-copied', {
  194. organization: params.organization,
  195. })
  196. }
  197. >
  198. {params.dsn.public}
  199. </TextCopyInput>
  200. )}
  201. </Fragment>
  202. ),
  203. },
  204. ],
  205. };
  206. const replayOnboarding: OnboardingConfig = {
  207. install: (params: Params) => [
  208. {type: StepType.INSTALL, configurations: getInstallConfig(params)},
  209. ],
  210. configure: (params: Params) => [
  211. {
  212. type: StepType.CONFIGURE,
  213. description: getReplayConfigureDescription({
  214. link: 'https://docs.sentry.io/platforms/javascript/guides/nextjs/session-replay/',
  215. }),
  216. configurations: [
  217. {
  218. code: [
  219. {
  220. label: 'sentry.client.config.js',
  221. value: 'javascript',
  222. language: 'javascript',
  223. code: getReplaySDKSetupSnippet({
  224. importStatement: `import * as Sentry from "@sentry/nextjs";`,
  225. dsn: params.dsn.public,
  226. mask: params.replayOptions?.mask,
  227. block: params.replayOptions?.block,
  228. }),
  229. },
  230. ],
  231. },
  232. ],
  233. additionalInfo: (
  234. <Fragment>
  235. <TracePropagationMessage />
  236. {tct(
  237. 'Note: The Replay integration only needs to be added to your [code:sentry.client.config.js] file. Adding it to any server-side configuration files (like [code:instrumentation.ts]) will break your build because the Replay integration depends on Browser APIs.',
  238. {
  239. code: <code />,
  240. }
  241. )}
  242. </Fragment>
  243. ),
  244. },
  245. ],
  246. verify: getReplayVerifyStep(),
  247. nextSteps: () => [],
  248. };
  249. const feedbackOnboarding: OnboardingConfig = {
  250. install: (params: Params) => [
  251. {
  252. type: StepType.INSTALL,
  253. description: tct(
  254. 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/nextjs]) installed, minimum version 7.85.0.',
  255. {
  256. code: <code />,
  257. }
  258. ),
  259. configurations: getInstallConfig(params),
  260. },
  261. ],
  262. configure: (params: Params) => [
  263. {
  264. type: StepType.CONFIGURE,
  265. description: getFeedbackConfigureDescription({
  266. linkConfig:
  267. 'https://docs.sentry.io/platforms/javascript/guides/nextjs/user-feedback/configuration/',
  268. linkButton:
  269. 'https://docs.sentry.io/platforms/javascript/guides/nextjs/user-feedback/configuration/#bring-your-own-button',
  270. }),
  271. configurations: [
  272. {
  273. code: [
  274. {
  275. label: 'sentry.client.config.js',
  276. value: 'javascript',
  277. language: 'javascript',
  278. code: getFeedbackSDKSetupSnippet({
  279. importStatement: `import * as Sentry from "@sentry/nextjs";`,
  280. dsn: params.dsn.public,
  281. feedbackOptions: params.feedbackOptions,
  282. }),
  283. },
  284. ],
  285. },
  286. ],
  287. additionalInfo: (
  288. <AdditionalInfoWrapper>
  289. <div>
  290. {tct(
  291. 'Note: The User Feedback integration only needs to be added to your [code:sentry.client.config.js] file. Adding it to any server-side configuration files (like [code:instrumentation.ts]) will break your build because the Replay integration depends on Browser APIs.',
  292. {
  293. code: <code />,
  294. }
  295. )}
  296. </div>
  297. <div>
  298. {crashReportCallout({
  299. link: 'https://docs.sentry.io/platforms/javascript/guides/nextjs/user-feedback/#crash-report-modal',
  300. })}
  301. </div>
  302. </AdditionalInfoWrapper>
  303. ),
  304. },
  305. ],
  306. verify: () => [],
  307. nextSteps: () => [],
  308. };
  309. const crashReportOnboarding: OnboardingConfig = {
  310. introduction: () => getCrashReportModalIntroduction(),
  311. install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
  312. configure: () => [
  313. {
  314. type: StepType.CONFIGURE,
  315. description: getCrashReportModalConfigDescription({
  316. link: 'https://docs.sentry.io/platforms/javascript/guides/nextjs/user-feedback/configuration/#crash-report-modal',
  317. }),
  318. additionalInfo: widgetCallout({
  319. link: 'https://docs.sentry.io/platforms/javascript/guides/nextjs/user-feedback/#user-feedback-widget',
  320. }),
  321. },
  322. ],
  323. verify: () => [],
  324. nextSteps: () => [],
  325. };
  326. const performanceOnboarding: OnboardingConfig = {
  327. introduction: () =>
  328. t(
  329. "Adding Performance to your React project is simple. Make sure you've got these basics down."
  330. ),
  331. install: params => [
  332. {
  333. type: StepType.INSTALL,
  334. description: t('Install the Next.js SDK using our installation wizard:'),
  335. configurations: [
  336. {
  337. language: 'bash',
  338. code: getInstallSnippet(params),
  339. },
  340. ],
  341. },
  342. ],
  343. configure: params => [
  344. {
  345. type: StepType.CONFIGURE,
  346. description: tct(
  347. 'To configure, set [code:tracesSampleRate] in your config files, [code:sentry.server.config.js], [code:sentry.client.config.js], and [code:sentry.edge.config.js]:',
  348. {code: <code />}
  349. ),
  350. configurations: [
  351. {
  352. language: 'javascript',
  353. code: `
  354. import * as Sentry from "@sentry/nextjs";
  355. Sentry.init({
  356. dsn: "${params.dsn.public}",
  357. // Set tracesSampleRate to 1.0 to capture 100%
  358. // of transactions for performance monitoring.
  359. // We recommend adjusting this value in production
  360. tracesSampleRate: 1.0,
  361. });
  362. `,
  363. additionalInfo: tct(
  364. '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 [linkSampleTransactions:sample transactions].',
  365. {
  366. code: <code />,
  367. linkTracingOptions: (
  368. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#tracing-options" />
  369. ),
  370. linkTracesSampler: (
  371. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/sampling/" />
  372. ),
  373. linkSampleTransactions: (
  374. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/sampling/" />
  375. ),
  376. }
  377. ),
  378. },
  379. ],
  380. },
  381. ],
  382. verify: () => [
  383. {
  384. type: StepType.VERIFY,
  385. description: tct(
  386. 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your NextJS application.',
  387. {
  388. link: (
  389. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/instrumentation/automatic-instrumentation/" />
  390. ),
  391. }
  392. ),
  393. additionalInfo: tct(
  394. 'You have the option to manually construct a transaction using [link:custom instrumentation].',
  395. {
  396. link: (
  397. <ExternalLink href="https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/instrumentation/custom-instrumentation/" />
  398. ),
  399. }
  400. ),
  401. },
  402. ],
  403. nextSteps: () => [],
  404. };
  405. const docs: Docs = {
  406. onboarding,
  407. feedbackOnboardingNpm: feedbackOnboarding,
  408. replayOnboarding,
  409. customMetricsOnboarding: getJSMetricsOnboarding({
  410. getInstallConfig: getManualInstallConfig,
  411. }),
  412. performanceOnboarding,
  413. crashReportOnboarding,
  414. };
  415. export default docs;
  416. const DSNText = styled('div')`
  417. margin-bottom: ${space(0.5)};
  418. `;
  419. const AdditionalInfoWrapper = styled('div')`
  420. display: flex;
  421. flex-direction: column;
  422. gap: ${space(2)};
  423. `;
  424. const Divider = styled('hr')`
  425. height: 1px;
  426. width: 100%;
  427. background: ${p => p.theme.border};
  428. border: none;
  429. margin-top: ${space(1)};
  430. margin-bottom: ${space(2)};
  431. `;