---
title: "Hydrogen with Remix (Legacy)"
description: "Learn how to use the Sentry Remix SDK to instrument your Hydrogen app (versions before 2025.5.0)."
url: https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix/
---

# Hydrogen with Remix (Legacy) for Cloudflare

##### Hydrogen Version

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](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router.md).

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.

## [Installing Sentry Remix and Cloudflare SDKs](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.md#installing-sentry-remix-and-cloudflare-sdks)

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

```bash
npm install @sentry/remix @sentry/cloudflare --save
```

*Other available variations of the above snippet: yarn, pnpm*

## [Instrumenting Your Server](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.md#instrumenting-your-server)

Update your `server.ts` file to use the `wrapRequestHandler` method from `@sentry/cloudflare/request` and `instrumentBuild` from `@sentry/remix/cloudflare`:

On Shopify Oxygen, never import anything directly from `@sentry/cloudflare`. Only use the `@sentry/cloudflare/request` subpath import shown below. All other imports (for example, `import * as Sentry from "@sentry/cloudflare"`) will break your app on Oxygen — use `@sentry/remix/cloudflare` for everything else.

##### No request isolation

`wrapRequestHandler` doesn't isolate requests from each other. Scope data you set during a request, for example with `setTag` or `setUser`, can show up on events from other concurrent or later requests in the same isolate. Use `wrapRequestHandler` only on runtimes that can't enable `nodejs_compat`, such as Shopify Oxygen. For Remix on Cloudflare Workers, follow the [Remix on Cloudflare guide](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/remix.md) instead.

```ts
import { wrapRequestHandler } from "@sentry/cloudflare/request";
import { instrumentBuild } from "@sentry/remix/cloudflare";
import { createRequestHandler } from "@remix-run/cloudflare";
// Virtual entry point for the app
import * as remixBuild from "virtual:remix/server-build";

/**
 * Export a fetch handler in module format.
 */
export default {
  async fetch(
    request: Request,
    env: Env,
    executionContext: ExecutionContext
  ): Promise<Response> {
    return wrapRequestHandler(
      {
        options: {
          dsn: "YOUR_DSN_HERE",
          tracesSampleRate: 1.0,
        },
        // Need to cast to any because this is not on cloudflare
        request: request as any,
        context: executionContext,
      },
      async () => {
        // Instrument your server build with Sentry
        // and use the instrumented build inside the fetch handler
        const instrumentedBuild = instrumentBuild(remixBuild);

        const handleRequest = createRequestHandler({
          build: instrumentedBuild,
          mode: process.env.NODE_ENV,
          getLoadContext: (): AppLoadContext => ({
            // your load context
          }),
        });

        return handleRequest(request);
      }
    );
  },
};
```

## [Instrumenting Your Client](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.md#instrumenting-your-client)

Wrap your Remix root component using `withSentry`:

```tsx
import * as Sentry from "@sentry/remix/cloudflare";
import { useEffect } from "react";
import { useLocation, useMatches } from "@remix-run/react";

function App() {
  return (
    // Your app content
  );
}

// Pass `useEffect`, `useLocation` and `useMatches` hooks to `withSentry`
export default Sentry.withSentry(App, useEffect, useLocation, useMatches);
```

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

```tsx
import * as Sentry from "@sentry/remix/cloudflare";
import { useEffect } from "react";
import { useLocation, useMatches } from "@remix-run/react";

Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  integrations: [
    Sentry.browserTracingIntegration({
      useEffect,
      useLocation,
      useMatches,
    }),
    // Replay is only available in the client
    Sentry.replayIntegration(),
  ],

  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for tracing.
  // We recommend adjusting this value in production
  // Learn more at
  // https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sample-rate
  tracesSampleRate: 1.0,

  // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
  tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],

  // Capture Replay for 10% of all sessions,
  // plus for 100% of sessions with an error
  // Learn more at
  // https://docs.sentry.io/platforms/javascript/session-replay/configuration/#general-integration-configuration
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});
```

## [Control the Data You Send to Sentry (Optional)](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.md#control-the-data-you-send-to-sentry-optional)

By default, the SDK sends user identity data (IP address, ID, and similar) and other data like HTTP bodies and URL query parameters. This will give you rich debugging context.

The SDK always filters sensitive values whose keys match a built-in denylist, such as `auth` or `password`, and sends `[Filtered]` instead.

To send less data, turn off the categories you don't need in the `dataCollection` option. For the full list of categories and their defaults, [see the `dataCollection` options](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/options.md#dataCollection).

```javascript
Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  dataCollection: {
    userInfo: false,
    // other categories
  },
});
```

## [Trace Propagation](https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.md#trace-propagation)

Available since: `v10.45.0`

Because Hydrogen uses a custom `entry.server.tsx` that bypasses the default Remix document request handler, the SDK cannot automatically inject trace propagation headers. To connect server-side traces with client-side `pageload` spans, you need to manually add the `Server-Timing` header using `generateSentryServerTimingHeader()`.

Update your `entry.server.tsx` to generate and append the header to your response:

```tsx
import { generateSentryServerTimingHeader } from "@sentry/remix/cloudflare";

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
  // ...

  const response = new Response(body, {
    headers: responseHeaders,
    status: responseStatusCode,
  });

  const serverTimingValue = generateSentryServerTimingHeader();
  if (serverTimingValue) {
    response.headers.append("Server-Timing", serverTimingValue);
  }

  return response;
}
```

The browser SDK automatically reads the `Server-Timing` header via the [Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API) and uses it to connect the client-side `pageload` span with the server-side transaction. No additional client-side configuration is needed beyond having `browserTracingIntegration` configured.
