Star us on GitHub
Star
Menu

Remix Walkthrough

Overview

Our Remix SDK gives you access to frontend session replays and server-side monitoring, all-in-one.

  • Use <HighlightInit /> to track session replay and client-side errors.
  • Use H.init to instrument Remix's nodejs server.

Installation

# with yarn yarn add @highlight-run/remix
Copy

Client Instrumentation

  • Inject <HighlightInit /> into your app root.
  • Optionally configure excludedHostnames to block a full or partial hostname. For example, excludedHostnames={['staging']} would not initialize Highlight on staging.highlight.io.
  • Configure tracingOrigins and networkRecording

See Fullstack Mapping for details.

// app/root.tsx import { useLoaderData } from '@remix-run/react' import { HighlightInit } from '@highlight-run/remix/client' import { json } from '@remix-run/node' export async function loader() { return json({ ENV: { HIGHLIGHT_PROJECT_ID: process.env.HIGHLIGHT_PROJECT_ID, }, }) } export default function App() { const { ENV } = useLoaderData() return ( <html lang="en"> <HighlightInit excludedHostnames={['localhost']} projectId={ENV.HIGHLIGHT_PROJECT_ID} serviceName="my-remix-frontend" tracingOrigins networkRecording={{ enabled: true, recordHeadersAndBody: true }} /> {/* Render head, body, <Outlet />, etc. */} </html> ) }
Copy
  • Optionally Create an ErrorBoundary component and export it from app/root.tsx
// app/components/error-boundary.tsx import { isRouteErrorResponse, useRouteError } from '@remix-run/react' import { ReportDialog } from '@highlight-run/remix/report-dialog' export function ErrorBoundary() { const error = useRouteError() if (isRouteErrorResponse(error)) { return ( <div> <h1> {error.status} {error.statusText} </h1> <p>{error.data}</p> </div> ) } else if (error instanceof Error) { return ( <div> <script src="https://unpkg.com/highlight.run"></script> <script dangerouslySetInnerHTML={{ __html: ` H.init('\${process.env.HIGHLIGHT_PROJECT_ID}'); `, }} /> <h1>Error</h1> <p>{error.message}</p> <p>The stack trace is:</p> <pre>{error.stack}</pre> <ReportDialog /> </div> ) } else { return <h1>Unknown Error</h1> } }
Copy
// app/root.tsx export { ErrorBoundary } from '~/components/error-boundary'
Copy

Server Instrumentation

  • Use H.init from @highlight-run/remix/server to instrument the Remix server on Node.js.
  • Import HandleError from @highlight-run/remix/server and export handleError after setting nodeOptions.
// app/entry.server.tsx import { HandleError } from '@highlight-run/remix/server' const nodeOptions = { projectID: process.env.HIGHLIGHT_PROJECT_ID } export const handleError = HandleError(nodeOptions) // Handle server requests
Copy

Alternatively, you can wrap Highlight's error handler and execute your own custom error handling code as well.

// app/entry.server.tsx import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node' import { H, HandleError } from '@highlight-run/remix/server' const nodeOptions = { projectID: process.env.HIGHLIGHT_PROJECT_ID } export function handleError( error: unknown, dataFunctionArgs: LoaderFunctionArgs | ActionFunctionArgs, ) { const handleError = HandleError(nodeOptions) handleError(error, dataFunctionArgs) // custom error handling logic here } H.init(nodeOptions) // Handle server requests
Copy

Handle streaming HTML responses using the onError handler of renderToPipeableStream

function handleBrowserRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, ) { return new Promise((resolve, reject) => { let shellRendered = false const { pipe, abort } = renderToPipeableStream( <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, { onShellReady() { shellRendered = true const body = new PassThrough() responseHeaders.set('Content-Type', 'text/html') resolve( new Response(body, { headers: responseHeaders, status: responseStatusCode, }), ) pipe(body) }, onShellError(error: unknown) { reject(error) }, onError(error: unknown) { if (shellRendered) { logError(error, request) } }, }, ) setTimeout(abort, ABORT_DELAY) }) } function logError(error: unknown, request?: Request) { const parsed = request ? H.parseHeaders(Object.fromEntries(request.headers)) : undefined if (error instanceof Error) { H.consumeError(error, parsed?.secureSessionId, parsed?.requestId) } else { H.consumeError( new Error(`Unknown error: ${JSON.stringify(error)}`), parsed?.secureSessionId, parsed?.requestId, ) } console.error(error) }
Copy