Remix Integration
Full-stack observability for Remix with one npm install. Loader and action tracing, nested route parallel fetch visualization, Prisma query spans, and client-side navigation metrics.
How It Works
Install the npm Package
Run npm install @tigerops/remix. The package includes a Remix-compatible server entry instrumentation hook, a client-side browser SDK for navigation tracing, and OTLP exporters for both Node.js and edge runtimes.
Wrap the Server Entry
In entry.server.tsx, wrap the default handleRequest export with TigerOps.wrapEntryServer(). This intercepts the Remix request handler to inject trace context into every SSR cycle. For Cloudflare Workers, use the edge-compatible wrapEntryServerEdge() variant.
Configure API Key & Service Name
Set TIGEROPS_API_KEY and TIGEROPS_SERVICE_NAME in your environment or .env file. In entry.client.tsx, call TigerOps.initBrowser() with the same service name to enable client-side navigation and Web Vitals collection.
Loader, Action & Navigation Traces
TigerOps begins capturing loader execution spans per route segment, action mutation spans, nested route parallel fetch timing, client-side navigation spans, Prisma or Drizzle query traces, and Core Web Vitals per route within seconds.
What You Get Out of the Box
Loader Function Tracing
Every Remix loader function execution creates a span with route ID, loader execution time, returned data size, and any thrown Response or redirect. Nested route loaders running in parallel appear as concurrent sibling spans in the trace waterfall.
Action Function Tracing
Form submission actions are traced with route ID, HTTP method, action execution time, and any validation errors thrown as Response objects. The action span is linked to the originating loader span for complete request-to-mutation trace continuity.
Nested Route Parallel Fetch Insights
Remix fetches data for all matched route segments in parallel. TigerOps visualizes each segment's loader as a concurrent span with individual start/end times, making it easy to identify the slowest loader that determines total page data load time.
Client-Side Navigation Spans
Remix client-side navigations via Link and navigate() create browser spans with source route, target route, navigation type (push/replace/reload), and prefetch hit/miss status. Transition state durations are measured from user interaction to settled.
Prisma & Drizzle Query Spans
Database queries executed inside Remix loaders and actions via Prisma or Drizzle ORM are auto-instrumented as child spans. Each span includes the operation type, model name, normalized SQL, execution time, and row count.
SSR Rendering & Hydration Timing
Server-side rendering time per page is captured as a span attribute. Client-side hydration duration and any hydration mismatch errors are tracked in the browser span. Time-to-interactive after Remix hydration is reported as a Core Web Vital.
Install & Initialize
One npm install and two entry file wrappers. Full Remix observability.
# Install the TigerOps Remix package
npm install @tigerops/remix
# Set environment variables
export TIGEROPS_API_KEY="your-api-key"
export TIGEROPS_SERVICE_NAME="my-remix-app"
// entry.server.tsx
import { TigerOps } from "@tigerops/remix/server";
export default TigerOps.wrapEntryServer(
async (request, responseStatusCode, responseHeaders, remixContext) => {
const { renderToReadableStream } = await import("react-dom/server");
const body = await renderToReadableStream(
<RemixServer context={remixContext} url={request.url} />,
);
return new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
});
},
{ apiKey: process.env.TIGEROPS_API_KEY!, serviceName: "my-remix-app" }
);
// entry.client.tsx
import { TigerOps } from "@tigerops/remix/client";
TigerOps.initBrowser({
serviceName: "my-remix-app",
webVitals: true,
traceNavigation: true,
});
// Custom span in a loader
import { tigerops } from "@tigerops/remix/server";
import type { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({ request, params }: LoaderFunctionArgs) {
return tigerops.span("order.loader", async (span) => {
span.setAttribute("order.id", params.id!);
const order = await db.order.findUniqueOrThrow({
where: { id: params.id },
});
return json(order);
});
}Common Questions
Which Remix versions are supported?
Remix 2.x (Vite-based) and Remix v1.x are fully supported. The package works with all Remix adapter targets: Express, Cloudflare Workers, Cloudflare Pages, Vercel, Netlify, and Deno. React 18+ is required for the browser SDK.
How does TigerOps handle Remix loaders that throw redirect responses?
Redirects thrown via the Remix redirect() helper are captured as spans with status code 3xx and the redirect target URL. The span is marked as a successful redirect, not an error, and the redirect chain is visible in the trace timeline.
Can I add custom spans inside Remix loader and action functions?
Yes. Use tigerops.span() inside any loader or action. The context is automatically available from the request headers injected by the server entry wrapper. Call tigerops.spanFromRequest(request) to get the current span context in a loader.
Does TigerOps work with Remix deployed to Cloudflare Workers?
Yes. Use the edge-compatible tigerops.initEdge() and wrapEntryServerEdge() exports which use the fetch-based OTLP exporter and Cloudflare waitUntil() for non-blocking span export. The full SDK is under 8KB gzipped for edge bundles.
How are server-side loader spans linked to client-side navigation spans?
The server entry wrapper injects the server trace ID into the Remix meta tags on the initial HTML response. The browser SDK reads the server trace ID during hydration and creates client navigation spans as children of the server request span for full-stack trace continuity.
Full Remix Observability in One npm Install
Loader and action traces, nested route fetch visualization, Prisma spans, and client navigation metrics — no code changes required.