---
title: "Remix 3 (Alpha)"
description: "Learn how to set up Sentry in a Remix 3 app, upload source maps, and capture your first errors."
url: https://docs.sentry.io/platforms/javascript/guides/remix/remix-3/
---

# Remix 3 (Alpha)

##### Alpha

Support for Remix 3 is in alpha. The API can change in any release. It lives under the `@sentry/remix/v3` subpaths and is separate from the Remix 2 SDK documented on the other pages of this guide. Please [report issues](https://github.com/getsentry/sentry-javascript/issues/new/choose) you run into.

Remix 3 has no build step: the asset server compiles browser modules per request. The SDK works with that instead of a bundler plugin. A module hook instruments the Remix server packages when Node imports them, and the asset server is instrumented so the browser modules it serves carry debug IDs.

## [Install](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#install)

Install the SDK. It requires `remix@3.0.0-rc.1` or later and Node 20.19 or later.

```bash
npm install @sentry/remix
```

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

## [Configure the Server](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#configure-the-server)

### [Start With the Sentry Entry](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#start-with-the-sentry-entry)

Start the app with `--import @sentry/remix/v3/node` instead of `--import remix/node-tsx`. The Sentry entry registers the module hook before your app's modules are imported, then loads `remix/node-tsx` for you.

```json
{
  "scripts": {
    "start": "node --import @sentry/remix/v3/node server.ts"
  }
}
```

### [Initialize the SDK](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#initialize-the-sdk)

Call `init()` in your server entry.

Each request gets a span named after the matched route pattern. Errors thrown in route handlers, middleware and the request listener are reported. Requests the client aborted are not, and neither are thrown responses with a status from 300 to 499. Pass `shouldHandleError` to `remixV3Integration()` to change that.

```ts
import * as Sentry from "@sentry/remix/v3";
import * as http from "node:http";
import { createRequestListener } from "remix/node-fetch-server";

import { router } from "./app/router.ts";

Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  // Adjust this value in production, or use tracesSampler for greater control.
  tracesSampleRate: 1.0,
});

http
  .createServer(createRequestListener((request) => router.fetch(request)))
  .listen(3000);
```

## [Configure the Browser](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#configure-the-browser)

### [Allow the SDK in the Asset Server](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#allow-the-sdk-in-the-asset-server)

Add `@sentry/remix` to `allowPackages` so the asset server can serve the SDK to the browser.

There is no build step to inline environment variables, so use `define` to get the DSN into browser modules. The asset server substitutes these values when it compiles a module.

```ts
import { createAssetServer } from "remix/assets";

export const assets = createAssetServer({
  basePath: "/assets",
  rootDir: process.cwd(),
  allowFiles: ["app/**/public/**"],
  allowPackages: ["remix", "@sentry/remix"],
  scripts: {
    define: {
      "process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN),
    },
  },
});
```

### [Initialize the SDK](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#initialize-the-sdk-1)

Call `init()` in your browser entry, before `run()`.

Page loads and navigations get a span each. Errors thrown while a component renders are reported; the Remix runtime does not rethrow those, so nothing else would see them.

```ts
import * as Sentry from "@sentry/remix/v3/client";
import { run } from "remix/ui";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  // Adjust this value in production, or use tracesSampler for greater control.
  tracesSampleRate: 1.0,
});

export const app = run({
  async loadModule(moduleUrl, exportName) {
    const mod = await import(moduleUrl);
    return mod[exportName];
  },
});
```

## [Upload Source Maps](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#upload-source-maps)

Browser modules served by the asset server carry debug IDs, so stack traces are symbolicated once the source maps are uploaded. Remix 3 has no build output to upload, so the SDK ships a command that compiles the browser module graph through your asset server and uploads it.

Run it from your deploy pipeline, with the same code and configuration the server runs. The debug IDs are hashed from the compiled source, so what is uploaded matches what the running server serves.

```bash
node --import @sentry/remix/v3/node ./node_modules/.bin/sentry-remix-v3-upload-sourcemaps --entry app/entry.ts --project <your-project-slug>
```

The command reads `SENTRY_AUTH_TOKEN` and `SENTRY_ORG` from the environment, and proposes the release from your git history. `--project` has to be passed. Pass `--org` or `--release` to set those yourself.

`--entry` is a browser entry path and can be repeated. The asset server is read from `./app/assets.ts` (export `assets`); set `--assets-module` and `--assets-export` if yours lives elsewhere. `--dry-run` compiles without uploading.

Source maps are generated but hidden by default: modules do not reference them and `.map` requests are not served, so the source only reaches Sentry. Set `sourceMaps: "external"` on the asset server to serve them, or `sourceMaps: false` to turn them off.

## [Verify](https://docs.sentry.io/platforms/javascript/guides/remix/remix-3.md#verify)

Start the app and throw an error from a route handler and from a browser component. Both should show up as issues in your project, the browser one with a readable stack trace once the source maps are uploaded.
