Skip to content

Commit 250a812

Browse files
authored
docs(js): Add docs for Hydrogen with RR7 (#14613)
1 parent 8ad4ae0 commit 250a812

File tree

2 files changed

+236
-15
lines changed

2 files changed

+236
-15
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
---
2+
title: Hydrogen with React Router
3+
description: Learn how to use the Sentry React Router SDK to instrument your Hydrogen app (versions 2025.5.0+).
4+
---
5+
6+
<Alert level="info" title="Hydrogen Version">
7+
8+
This guide applies to Hydrogen versions **2025.5.0 and later** that use React Router 7 (framework mode). For older versions of Hydrogen that use Remix v2, see the [Remix guide](/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix/).
9+
10+
</Alert>
11+
12+
Starting from Hydrogen version 2025.5.0, Shopify switched from Remix v2 to React Router 7 (framework mode). You can use the Sentry React Router SDK with Cloudflare support to add Sentry instrumentation to your Hydrogen app.
13+
14+
## Installing Sentry React Router and Cloudflare SDKs
15+
16+
First, install the Sentry React Router and Cloudflare SDKs with your package manager:
17+
18+
```bash {tabTitle:npm}
19+
npm install @sentry/react-router @sentry/cloudflare --save
20+
```
21+
22+
```bash {tabTitle:yarn}
23+
yarn add @sentry/react-router @sentry/cloudflare
24+
```
25+
26+
```bash {tabTitle:pnpm}
27+
pnpm add @sentry/react-router @sentry/cloudflare
28+
```
29+
30+
## Instrumenting Your Server
31+
32+
Create an `instrument.server.mjs` file to initialize Sentry on the server:
33+
34+
```js {filename:instrument.server.mjs}
35+
import * as Sentry from "@sentry/react-router";
36+
37+
Sentry.init({
38+
dsn: "YOUR_DSN_HERE",
39+
// Adds request headers and IP for users, for more info visit:
40+
// https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii
41+
sendDefaultPii: true,
42+
tracesSampleRate: 1.0,
43+
});
44+
```
45+
46+
Update your `server.ts` file to use the `wrapRequestHandler` method from `@sentry/cloudflare`:
47+
48+
```ts {filename:server.ts}
49+
import { wrapRequestHandler } from '@sentry/cloudflare';
50+
// ...other imports
51+
52+
/**
53+
* Export a fetch handler in module format.
54+
*/
55+
export default {
56+
async fetch(
57+
request: Request,
58+
env: Env,
59+
executionContext: ExecutionContext
60+
): Promise<Response> {
61+
return wrapRequestHandler(
62+
{
63+
options: {
64+
dsn: "YOUR_DSN_HERE",
65+
tracesSampleRate: 1.0,
66+
},
67+
request: request as any,
68+
context: executionContext,
69+
},
70+
async () => {
71+
// Your existing Hydrogen server logic
72+
const handleRequest = createRequestHandler({
73+
// @ts-ignore
74+
build: await import('virtual:react-router/server-build'),
75+
mode: process.env.NODE_ENV,
76+
getLoadContext: (): AppLoadContext => ({
77+
// your load context
78+
}),
79+
});
80+
81+
return handleRequest(request);
82+
}
83+
);
84+
},
85+
};
86+
```
87+
88+
## Instrumenting Your Client
89+
90+
Initialize Sentry in your `entry.client.tsx` file:
91+
92+
```tsx {filename:app/entry.client.tsx}
93+
import { HydratedRouter } from 'react-router/dom';
94+
import * as Sentry from '@sentry/react-router/cloudflare';
95+
import { StrictMode, startTransition } from 'react';
96+
import { hydrateRoot } from 'react-dom/client';
97+
98+
Sentry.init({
99+
dsn: "___PUBLIC_DSN___",
100+
integrations: [Sentry.reactRouterTracingIntegration()],
101+
tracesSampleRate: 1.0,
102+
});
103+
104+
startTransition(() => {
105+
hydrateRoot(
106+
document,
107+
<StrictMode>
108+
<HydratedRouter />
109+
</StrictMode>,
110+
);
111+
});
112+
```
113+
114+
## Server-Side Rendering with Trace Injection
115+
116+
To enable distributed tracing, wrap your `handleRequest` function in your `entry.server.tsx` file and inject trace meta tags:
117+
118+
```tsx {filename:app/entry.server.tsx}
119+
import './instrument.server';
120+
import { HandleErrorFunction, ServerRouter } from 'react-router';
121+
import type { EntryContext } from '@shopify/remix-oxygen';
122+
import { renderToReadableStream } from 'react-dom/server';
123+
import * as Sentry from '@sentry/react-router/cloudflare';
124+
125+
async function handleRequest(
126+
request: Request,
127+
responseStatusCode: number,
128+
responseHeaders: Headers,
129+
reactRouterContext: EntryContext,
130+
) {
131+
const body = Sentry.injectTraceMetaTags(await renderToReadableStream(
132+
<ServerRouter context={reactRouterContext} url={request.url} />,
133+
{
134+
signal: request.signal,
135+
},
136+
));
137+
138+
responseHeaders.set('Content-Type', 'text/html');
139+
140+
return new Response(body, {
141+
headers: responseHeaders,
142+
status: responseStatusCode,
143+
});
144+
}
145+
146+
export const handleError: HandleErrorFunction = (error, { request }) => {
147+
// React Router may abort some interrupted requests, don't log those
148+
if (!request.signal.aborted) {
149+
Sentry.captureException(error);
150+
console.error(error);
151+
}
152+
};
153+
154+
export default Sentry.wrapSentryHandleRequest(handleRequest);
155+
```
156+
157+
## Configuration
158+
159+
### Vite Configuration
160+
161+
Add the Sentry plugin to your `vite.config.ts`:
162+
163+
```ts {filename:vite.config.ts}
164+
import { reactRouter } from '@react-router/dev/vite';
165+
import { hydrogen } from '@shopify/hydrogen/vite';
166+
import { oxygen } from '@shopify/mini-oxygen/vite';
167+
import { defineConfig } from 'vite';
168+
import { sentryReactRouter } from '@sentry/react-router';
169+
170+
export default defineConfig(config => ({
171+
plugins: [
172+
hydrogen(),
173+
oxygen(),
174+
reactRouter(),
175+
sentryReactRouter({
176+
org: "your-org-slug",
177+
project: "your-project-slug",
178+
authToken: process.env.SENTRY_AUTH_TOKEN,
179+
}, config),
180+
// ... other plugins
181+
],
182+
}));
183+
```
184+
185+
### Build Configuration
186+
187+
Add the `buildEnd` hook to your `react-router.config.ts`:
188+
189+
```ts {filename:react-router.config.ts}
190+
import type {Config} from '@react-router/dev/config';
191+
import { sentryOnBuildEnd } from '@sentry/react-router';
192+
193+
export default {
194+
appDirectory: 'app',
195+
buildDirectory: 'dist',
196+
ssr: true,
197+
buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => {
198+
// Call this at the end of the hook
199+
(await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest }));
200+
}
201+
} satisfies Config;
202+
```

docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen.mdx renamed to docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.mdx

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
---
2-
title: Hydrogen Guide
3-
description: "Learn how to use the Sentry Remix SDK to instrument your Hydrogen app."
2+
title: Hydrogen with Remix (Legacy)
3+
description: Learn how to use the Sentry Remix SDK to instrument your Hydrogen app (versions before 2025.5.0).
44
---
55

6-
If you're using the Shopify's Hydrogen framework, you can use the Sentry Remix SDK to add Sentry instrumentation to your app.
6+
<Alert level="info" title="Hydrogen Version">
77

8-
## 1. Installing Sentry Remix and Clouflare SDKs
8+
This guide applies to Hydrogen versions **before 2025.5.0** that use Remix v2. For newer versions of Hydrogen (2025.5.0+) that use React Router 7, see the [React Router guide](/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router/).
9+
10+
</Alert>
11+
12+
If you're using Shopify's Hydrogen framework with Remix v2, you can use the Sentry Remix SDK to add Sentry instrumentation to your app.
13+
14+
## Installing Sentry Remix and Cloudflare SDKs
915

1016
First, install the Sentry Remix and Cloudflare SDKs with your package manager:
1117

@@ -21,13 +27,14 @@ yarn add @sentry/remix @sentry/cloudflare
2127
pnpm add @sentry/remix @sentry/cloudflare
2228
```
2329

24-
## 2. Instrumenting Your Server
30+
## Instrumenting Your Server
2531

26-
Then update your `server.ts` file to use the `wrapRequestHandler` method:
32+
Update your `server.ts` file to use the `wrapRequestHandler` method from `@sentry/cloudflare/request` and `instrumentBuild` from `@sentry/remix/cloudflare`:
2733

2834
```ts {filename:server.ts}
2935
import { wrapRequestHandler } from "@sentry/cloudflare/request";
3036
import { instrumentBuild } from "@sentry/remix/cloudflare";
37+
import { createRequestHandler } from "@remix-run/cloudflare";
3138
// Virtual entry point for the app
3239
import * as remixBuild from 'virtual:remix/server-build';
3340

@@ -53,33 +60,45 @@ export default {
5360
async () => {
5461
// Instrument your server build with Sentry
5562
// and use the instrumented build inside the fetch handler
56-
const instrumentedBuild = instrumentBuild(remixBuild)
63+
const instrumentedBuild = instrumentBuild(remixBuild);
64+
65+
const handleRequest = createRequestHandler({
66+
build: instrumentedBuild,
67+
mode: process.env.NODE_ENV,
68+
getLoadContext: (): AppLoadContext => ({
69+
// your load context
70+
}),
71+
});
5772

58-
// request handling logic
73+
return handleRequest(request);
5974
}
6075
);
6176
},
6277
};
6378
```
6479

65-
## 3. Instrumenting Your Client
80+
## Instrumenting Your Client
6681

6782
Wrap your Remix root component using `withSentry`:
6883

69-
```tsx {filename:root.tsx}
70-
import { withSentry } from "@sentry/remix/cloudflare";
84+
```tsx {filename:app/root.tsx}
85+
import * as Sentry from "@sentry/remix/cloudflare";
86+
import { useEffect } from "react";
87+
import { useLocation, useMatches } from "@remix-run/react";
7188

72-
function App({ children }) {
73-
return <>{children}</>;
89+
function App() {
90+
return (
91+
// Your app content
92+
);
7493
}
7594

7695
// Pass `useEffect`, `useLocation` and `useMatches` hooks to `withSentry`
77-
export default withSentry(App, useEffect, useLocation, useMatches);
96+
export default Sentry.withSentry(App, useEffect, useLocation, useMatches);
7897
```
7998

8099
Finally, update your `entry.client.tsx` file to initialize Sentry SDK on the client:
81100

82-
```tsx {filename:entry.client.tsx}
101+
```tsx {filename:app/entry.client.tsx}
83102
import * as Sentry from "@sentry/remix";
84103

85104
Sentry.init({

0 commit comments

Comments
 (0)