@browsonic/nextjs
v1.3.0
Published
Next.js adapter for @browsonic/sdk — App Router error pages, route-handler capture, instrumentation entry. Re-exports @browsonic/react. Apache-2.0.
Maintainers
Readme
@browsonic/nextjs
Next.js adapter for @browsonic/sdk — App Router error-page components (with optional pathname / params context), route-handler capture wrapper, Pages Router companions (browsonicPagesAppInit / browsonicPagesErrorInitialProps), config wrapper, plus all the React-side primitives re-exported from @browsonic/react.
Status: App Router
BrowsonicErrorPage/BrowsonicGlobalErrorPageaccept optionalpathname+paramsprops that consumers thread fromusePathname()/useParams()and land as thenextjs.pathnametag plus aparamssub-key on the consolidatednextjscontext bucket. Pages Router companions ship forpages/_app.tsx(browsonicPagesAppInit) andpages/_error.tsx(browsonicPagesErrorInitialProps). Theinstrumentation.tshelper (@browsonic/nextjs/instrumentation) ships. Build-time source-map upload is handled by@browsonic/build-tools— Next.js builds with Webpack, so drop its Webpack plugin intonext.config(seenext.config.jsbelow);withBrowsonicConfigstays a forward-compat passthrough.
Why this adapter
Next.js's App Router has framework-specific error surfaces that the React adapter alone doesn't cover:
app/error.tsxis rendered by Next.js when a route subtree throws. The component is a Client Component that receives{ error, reset }. We ship a drop-in for it.app/global-error.tsxowns the<html>/<body>shell and is rendered when the root layout itself crashes. We ship a drop-in for it too.app/api/.../route.tsroute handlers run server-side. A wrapper forwards thrown errors to the SDK (when reachable) before re-throwing them so Next.js can serve its 500.
This package depends on @browsonic/react and re-exports its surface so Next.js consumers install one package, not two.
Install
npm install @browsonic/sdk @browsonic/react @browsonic/nextjs@browsonic/sdk, @browsonic/react, next (≥13.4), and react (18+) are all peer dependencies.
Quickstart — App Router error pages
// app/error.tsx
'use client';
import { BrowsonicErrorPage } from '@browsonic/nextjs';
export default BrowsonicErrorPage;// app/global-error.tsx
'use client';
import { BrowsonicGlobalErrorPage } from '@browsonic/nextjs';
export default BrowsonicGlobalErrorPage;The components capture { error, digest } to the SDK on mount, then render a minimal "Something went wrong" UI with a Try Again button. To customise, copy the 30-line implementation from src/error-page.tsx and adjust the JSX.
To attach route-context to captured errors, wrap the default export with pathname and params from Next's hooks:
// app/error.tsx
'use client';
import { usePathname, useParams } from 'next/navigation';
import { BrowsonicErrorPage } from '@browsonic/nextjs';
export default function ErrorPage(props: {
error: Error & { digest?: string };
reset: () => void;
}) {
return <BrowsonicErrorPage {...props} pathname={usePathname()} params={useParams()} />;
}The boundary tags the captured event with nextjs.pathname and lands params as a sub-key on the single consolidated nextjs context bucket (alongside runtime / source / pathname) so dashboards can group errors by route shape.
Quickstart — Pages Router (Next ≤ 12 / opt-in 13+)
For projects on the Pages Router, two companions cover the equivalent surfaces:
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { useEffect } from 'react';
import { browsonicPagesAppInit } from '@browsonic/nextjs';
export default function App({ Component, pageProps }: AppProps) {
useEffect(() => browsonicPagesAppInit(), []);
return <Component {...pageProps} />;
}// pages/_error.tsx
import type { NextPage } from 'next';
import { browsonicPagesErrorInitialProps } from '@browsonic/nextjs';
interface ErrorProps {
statusCode: number;
pagePath?: string;
}
const Error: NextPage<ErrorProps> = ({ statusCode }) => <div>Error {statusCode}</div>;
// `browsonicPagesErrorInitialProps` IS the getInitialProps: it captures the
// error (browser-side only) and returns `{ statusCode, pagePath }` synchronously.
Error.getInitialProps = browsonicPagesErrorInitialProps;
export default Error;browsonicPagesAppInit registers window-level error / unhandledrejection listeners on the client (call it from useEffect; it returns a teardown so the listeners are removed on unmount / fast refresh) and is a no-op on the server. browsonicPagesErrorInitialProps captures whatever Next put on ctx.err, sets the consolidated nextjs context bucket (runtime: 'browser', source: 'pages-router-error', plus statusCode / pagePath / asPath when present), tags nextjs.pagePath, and records nextjsStatusCode metadata — capture only fires browser-side (it no-ops during SSR).
Quickstart — Route handlers
// app/api/checkout/route.ts
import { withBrowsonicRouteHandler } from '@browsonic/nextjs';
export const POST = withBrowsonicRouteHandler(async (req: Request) => {
const data = await req.json();
if (!data.email) throw new Error('email required');
return Response.json({ ok: true });
});The wrapper forwards the thrown Error to sdk.captureError, tags it with nextjsRouteHandler: 'true', and re-throws — Next.js's normal 500 path is preserved.
Quickstart — instrumentation.ts (Next 13.4+)
Next.js's project-root instrumentation.ts file convention runs once at server startup (register()) and on every unhandled server error (onRequestError). The @browsonic/nextjs/instrumentation sub-entry ships a one-line wire-up:
// instrumentation.ts (project root, alongside `next.config.mjs`)
import { browsonicInstrumentation } from '@browsonic/nextjs/instrumentation';
const { register, onRequestError } = browsonicInstrumentation({
apiEndpoint: process.env.BROWSONIC_API_ENDPOINT,
appKey: process.env.BROWSONIC_APP_KEY,
environment: process.env.VERCEL_ENV ?? process.env.NODE_ENV,
});
export { register, onRequestError };What ships today:
register()validates thatapiEndpoint+appKeyare present and emits oneconsole.warnper missing field. Misconfiguration surfaces at server boot instead of silently shipping pages with no telemetry.onRequestError(error, request, context)forwards the error toconsole.errorwith a structurednextjs.*context object (nextjs.path,nextjs.routerKind,nextjs.routePath,nextjs.routeType, etc.). Tests / custom log sinks override the report path via thereportErroroption.
What ships later (without a code change in your instrumentation.ts):
- Real server-runtime capture. The SDK is a browser library today, so
onRequestErrorforwards toconsole.errorrather than the ingest endpoint; a server-runtime transport is future work, independent of source maps (browser-side source-map upload already ships — see below). BROWSONIC_INSTRUMENTATION_VERSIONalready tags emitted events so future dashboards can distinguish wire-up generations.
Server-only sub-entry — the main @browsonic/nextjs bundle has no server code.
Quickstart — next.config.js
// next.config.mjs
import { withBrowsonicConfig } from '@browsonic/nextjs';
export default withBrowsonicConfig({
reactStrictMode: true,
// your config
});Today withBrowsonicConfig is a forward-compat passthrough — adopt it now to pick up future config-level integrations without editing this file again. It does not upload source maps.
Source-map upload
The source-map pipeline shipped in May 2026. Next.js builds with Webpack, so upload your production maps by adding the @browsonic/build-tools Webpack plugin to the Next config's webpack hook:
// next.config.mjs
import { BrowsonicSourceMapsPlugin } from '@browsonic/build-tools/webpack';
const nextConfig = {
productionBrowserSourceMaps: true, // emit browser source maps at build time
webpack(config, { isServer }) {
if (!isServer) {
config.devtool = 'hidden-source-map'; // write .map files without advertising them to users
config.plugins.push(new BrowsonicSourceMapsPlugin({ appKey: 'web' }));
}
return config;
},
};
export default nextConfig;The release the plugin uploads under must match the SDK's clientVersion. See the @browsonic/build-tools README for the token, release, and env-var options.
Quickstart — Boundary inside Client Components
The full React surface re-exports through this package, so anywhere in your 'use client' tree you can:
'use client';
import { BrowsonicErrorBoundary, useUser } from '@browsonic/nextjs';
export function App() {
useUser({ id: 'u1' });
return (
<BrowsonicErrorBoundary fallback={(error, reset) => <div>{error.message}</div>}>
<Routes />
</BrowsonicErrorBoundary>
);
}Naming note
withBrowsonic is the React HOC (re-exported from @browsonic/react).
withBrowsonicConfig is the Next.js config wrapper (this package).
The split mirrors @sentry/nextjs's withSentryConfig pattern.
Defensive contract
Same as every other adapter:
- The host app must never crash because reporting failed.
- SDK calls are wrapped in
try { ... } catch {}. - All surfaces work without an SDK present (page still renders, route handler still throws upstream, config still passes through).
What this package does NOT do (yet)
- Sourcemap upload through
withBrowsonicConfig. Source-map upload itself ships today — wire the@browsonic/build-toolsWebpack plugin intonext.config(see above).withBrowsonicConfigstays a passthrough and does not upload maps. - Auto-detected
instrumentation.tsinjection. The shipped helper is opt-in (consumer pastes a 5-line wire-up). A build-time injector that creates the file automatically would need a transform on every project's root, which is more invasive than the consumer-opt-in convention. - Server-runtime capture. The SDK is a browser library; route-handler errors that occur in pure Node have no
windowto write to. The wrapper still re-throws so your handler returns its expected status. - Edge runtime instrumentation. Edge runtimes lack a stable global Browsonic singleton — adopt the SDK in the client layer and use the route-handler wrapper for opportunistic capture.
- Pages Router data layer instrumentation (
getServerSideProps/getStaticProps). Will be revisited only if Pages Router consumer demand surfaces.
License
Apache-2.0. See the repo root LICENSE and the package NOTICE.
