npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@browsonic/react

v1.4.3

Published

React adapter for @browsonic/sdk — error boundary, hooks, HOC, React Router instrumentation. Apache-2.0.

Downloads

680

Readme

@browsonic/react

npm version License: Apache 2.0 CI

React adapter for @browsonic/sdk. Catches the render errors that window.onerror cannot see.

An Error Boundary anywhere above the throw catches a render-time exception and renders a fallback tree — the error never reaches window, so the global handlers a plain @browsonic/sdk install relies on never see it. This adapter wires React's Error Boundary primitive to Browsonic so those errors get reported, with the React component stack attached. (With no boundary at all above the throw, React 19 does re-report the error to window via reportError — but the component stack is lost either way, and the tree still unmounts.)

npm install @browsonic/sdk @browsonic/react
import { getBrowsonic } from '@browsonic/sdk';
import { BrowsonicErrorBoundary } from '@browsonic/react';

// Use getBrowsonic(), not `new Browsonic()`: it publishes the singleton on
// `window.Browsonic`, which is where this adapter's hooks — and a
// <BrowsonicErrorBoundary> with no `sdk` prop — look it up. A hand-constructed
// instance is invisible to them and they silently report nothing.
const sdk = getBrowsonic();
sdk.init({
  // Origin only — the SDK appends `/v1/events` itself.
  apiEndpoint: 'https://your-ingest.example.com',
  appKey: 'your-app-key',
  // Publishable key, safe in the browser. NOT optional in practice: page-view
  // tracking is on by default and `init()` returns false with
  // "apiKey is required for page view tracking" unless you also set
  // `trackPageViews: false`.
  apiKey: 'pk_live_...',
});

function App() {
  return (
    <BrowsonicErrorBoundary
      sdk={sdk}
      fallback={(error, reset) => (
        <div role="alert">
          <p>Something went wrong: {error.message}</p>
          <button onClick={reset}>Try again</button>
        </div>
      )}
    >
      <YourApp />
    </BrowsonicErrorBoundary>
  );
}

What this adapter ships

  • <BrowsonicErrorBoundary> — render-time error capture; the required fallback may be a node or (error, reset) => node, and reset() clears the error state and re-renders the children. An error thrown inside a <Suspense> subtree below it (a lazy() chunk that renders and throws) reaches it too.
  • useBrowsonic() — singleton instance hook (resolved once at mount, stable for the lifetime of the component). Returns Browsonic | nullnull whenever the SDK is unreachable, so guard before calling into it.
  • useUser(user | null) — sets the user context on mount and again whenever the user's fields change (value-compared, not reference-compared). It does not clear on unmount — that would race with a sibling remount — so pass null when you want it cleared.
  • useCaptureError() — stable callback for try/catch sites and event handlers.
  • withBrowsonic(Component) — HOC that injects sdk as a prop, for class components that cannot consume hooks.
  • useTrackPageView(template, name?, navKey?) + routeTemplateFromMatches(matches) — React Router v6 / v7 page-view instrumentation with the authoritative route template (shipped in 1.3.0; navKey since 1.4.0). See Router instrumentation.
import { BrowsonicErrorBoundary, useBrowsonic, useUser, useCaptureError } from '@browsonic/react';

function App({ currentUser }) {
  // Stamps the user context onto events from here on. Unmounting does NOT
  // clear it — pass `null` on logout.
  useUser(currentUser ?? null);

  return (
    <BrowsonicErrorBoundary fallback={<ErrorScreen />}>
      <Checkout />
    </BrowsonicErrorBoundary>
  );
}

function Checkout() {
  const captureError = useCaptureError();
  const sdk = useBrowsonic();

  const buy = async () => {
    try {
      await api.buy();
    } catch (err) {
      // Event handlers don't reach Error Boundaries — capture manually.
      captureError(err as Error);
    }
  };

  return <button onClick={buy}>Buy</button>;
}

Router instrumentation (React Router v6 / v7)

The Atlas backend prefers an authoritative parameterized route template (/users/:id) over URL normalization — the regex normalizer fundamentally cannot recover templates for alphabetic slugs. React Router knows the template; routeTemplateFromMatches + useTrackPageView (in src/atlas.ts) hand it to the SDK without this package depending on react-router (structural shapes only, no new peer dependency).

// React Router 6+: compute matches once at the layout root.
import { matchRoutes, useLocation } from 'react-router-dom';
import { routeTemplateFromMatches, useTrackPageView } from '@browsonic/react';

function AtlasPageViews({ routes }: { routes: RouteObject[] }) {
  const location = useLocation();
  const matches = matchRoutes(routes, location);
  // location.key changes per NAVIGATION — without it, /products/1 →
  // /products/2 (same /products/:id template) would be deduped away.
  useTrackPageView(routeTemplateFromMatches(matches), undefined, location.key);
  return null;
}
  • routeTemplateFromMatches(matches) joins matched route paths into the parameterized template (/users/:id/orders); layout wrappers and index routes are skipped, :params and * splats pass through. Returns '' when nothing matched — Atlas then falls back to URL normalization.
  • useTrackPageView(template, name?, navKey?) fires trackPageView whenever the (template, name, navigation) triple changes; re-renders at the same route send nothing. The optional name feeds the SDK ≥ 3.13 screen-name channel. Pass navKey (react-router's location.key is ideal) so consecutive same-template navigations still count. No-op when the SDK singleton is unreachable; never throws.
  • Pairing with the SDK: either init with manualPageViews: true, or on SDK ≥ 3.14 just init with atlas: true — the first templated page view takes the channel over from organic URL tracking automatically, with no double counting.

This package is versioned independently of @browsonic/sdk — it is on 1.x while the SDK is on 3.x, so there is no @browsonic/react release that matches an SDK version number. Widening the @browsonic/sdk peer range ships here as a patch release; see CHANGELOG.md.

Compatibility

| Surface | Versions | | ----------------- | ---------- | | React | 18.x, 19.x | | @browsonic/sdk | ≥ 3.12.0 | | Node (build/test) | ≥ 20 |

The authoritative ranges live in package.json peerDependencies.

Privacy

The adapter does not collect data on its own — it forwards to the SDK, which carries Browsonic's privacy-first defaults. See PRIVACY.md in the SDK repo.

On a render error caught by the boundary — and only then — the adapter adds two things of its own, both scoped to that single capture via the SDK's withScope so they do not stick to later events:

  • a react context bucket: React's version string, plus the component stack truncated to 1024 chars;
  • that same truncated component stack again as componentStack event metadata.

The component stack is React's own string — component names and source locations — and never includes prop or state values.

License

Apache License 2.0 — see LICENSE (repo root) and NOTICE.