Quickstart
@marsadco/sdk is one package for Next.js and React Native / Expo. It captures unhandled errors, manual exceptions and network requests, then ships them to your Marsad project. The runtime is auto-detected — you just call init().
Install
Add the single package. It has zero runtime dependencies; React is an optional peer (only needed for the ErrorBoundary).
npm install @marsadco/sdknpx expo install @marsadco/sdkGet your key
Grab your project's ingest key from Settings → API keys (or the Get started page). It looks like bk_live_…. Set it as an env var — MARSAD_KEY (server), NEXT_PUBLIC_MARSAD_KEY (Next.js browser) or EXPO_PUBLIC_MARSAD_KEY (Expo) — and init() picks it up automatically, so you don't have to pass key at all. The endpoint defaults to the hosted ingest URL, so you normally don't set it either.
Initialize — Next.js
The @marsadco/sdk/nextjs helper is the simplest path — it reads MARSAD_KEY / NEXT_PUBLIC_MARSAD_KEY for you. Re-export register from instrumentation.ts for the server, and call initBrowser() from a small client component mounted once in your root layout.
export { register, onRequestError } from "@marsadco/sdk/nextjs";"use client";
import { useEffect } from "react";
import { initBrowser } from "@marsadco/sdk/nextjs";
export function MarsadInit() {
useEffect(() => initBrowser(), []);
return null;
}Render <MarsadInit /> once inside app/layout.tsx. The helper also re-exports ErrorBoundary. Prefer to wire it by hand? Call init() from @marsadco/sdk directly — it still reads the key from the env when you don't pass one.
Re-exporting onRequestError captures every server-side error Next catches — server actions, route handlers, RSC and SSR renders — that never reaches the global handler. For a single server action you can also wrap it: withMarsad(action) (or wrapRouteHandler(handler)) captures, flushes, and re-throws. Client navigation, click and console breadcrumbs are captured automatically — no wiring needed.
Initialize — Expo / React Native
Use @marsadco/sdk/expo — it reads EXPO_PUBLIC_MARSAD_KEY and re-exports the React Native ErrorBoundary. Call init() at your app entry, then wrap your tree in the boundary to catch render-time errors with the component stack.
import { init, ErrorBoundary } from "@marsadco/sdk/expo";
init(); // key from EXPO_PUBLIC_MARSAD_KEY
export default function App() {
return (
<ErrorBoundary fallback={<Fallback />}>
<RootNavigator />
</ErrorBoundary>
);
}React Native has no browser history to patch, so add navigation breadcrumbs by feeding the hook your current route — with Expo Router:
import { usePathname } from "expo-router";
import { useNavigationBreadcrumbs } from "@marsadco/sdk/expo";
export default function Layout() {
useNavigationBreadcrumbs(usePathname());
// …
}Verify it works
Send a test event. Within a few seconds it should appear, grouped, on your Issues page.
import { captureException } from "@marsadco/sdk";
const code = captureException(new Error("Marsad test event"));
console.log(code); // → "MAR-K7P2QX9M" — searchable in ⌘KcaptureException and captureMessage return a short reference code (MAR-…) you can surface to users; paste it into the dashboard's ⌘K palette to jump straight to the issue.
Session tracking is on by default too: the SDK pings a session on each app start and marks it crashed on an unhandled error, powering the crash-free rate on your Overview. Web Vitals (LCP/CLS/INP/FCP/TTFB) are captured automatically on the web and shown as a p75 card there. The event queue is persisted and retried with backoff, so events survive reloads, offline periods and crashes. Opt into tracesSampleRate and the SDK also records performance traces — pageloads, navigations and requests — viewable as waterfalls on the Performance page.
From there, attach setTag(key, value) / setTags({…}) to make events filterable by plan, region or tenant, and call captureFeedback({ message, eventCode }) with the reference code to tie a user's report to its issue. See the SDK reference for the full API.
Upload source maps
So minified frames resolve to your real files and line numbers, upload your source maps after each build with the bundled CLI. Run it in CI or a postbuild step; stacks are symbolicated server-side the first time an issue is viewed.
# authenticated by your ingest key (--key or $MARSAD_KEY)
# inject stamps each chunk with a debug id; upload ships the maps
npx marsad inject ./dist && npx marsad upload-sourcemaps ./distinject writes a debugId into every chunk and its map, so a stack resolves to the exact build with no release to keep in sync. Prefer matching by release instead? Skip inject and pass --release (or $MARSAD_RELEASE) — the SDK auto-detects it in CI from env vars like VERCEL_GIT_COMMIT_SHA or GITHUB_SHA, so pass the same value to the CLI.