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

@activescott/analytics

v0.3.4

Published

PostHog analytics integration for React Router apps — provider, SPA pageview tracking, user identification, and reverse proxy helper

Readme

@activescott/analytics

PostHog analytics integration for React Router apps — SPA pageview tracking, user identification, and a reverse proxy helper to avoid ad blockers.

Install

npm install @activescott/analytics posthog-js

posthog-js is a peer dependency so consumers control its version. When bumping the peer floor, check the posthog-js changelog for breaking changes to PostHogConfig or the posthog-js/react exports, then release a matching minor of this package.

Quick Start

1. Add the provider to your app

Mount it where loader data is available (in React Router v7, that means App, not Layout — Layout renders with undefined loader data on error boundaries, and error pages intentionally get no pageview).

For zero bundle cost when disabled, use LazyPostHogProvider: it renders null unless enabled with a key, otherwise suspense-loads the initialized tree with posthog-js in its own chunk. Mount it as a STABLE SIBLING of your UI — never as a conditional wrapper around it, which would remount the whole subtree once the chunk loads:

// app/root.tsx
import { LazyPostHogProvider } from "@activescott/analytics"

export function App() {
  const { posthogKey, isAdmin, user } = useLoaderData<typeof loader>()
  return (
    <>
      <LazyPostHogProvider
        enabled={posthogKey !== "" && !isAdmin}
        apiKey={posthogKey || undefined}
        options={{ api_host: "/ph", ui_host: "https://us.posthog.com" }}
        user={user ? { distinctId: user.id } : undefined}
      />
      <Outlet />
    </>
  )
}

Identify by stable id only — never pass emails, phone numbers, or other PII in properties unless your privacy policy explicitly covers it.

PostHogProvider (same props plus children) remains for apps that prefer a static import and a wrapping provider.

Custom events

Capture through captureAnalyticsEvent or the useAnalyticsCapture hook — both read the client the provider registers on init, so they work anywhere without a provider ancestor, and drop while disabled or still loading:

import { useAnalyticsCapture } from "@activescott/analytics"

const capture = useAnalyticsCapture()
capture("search_results_shown", { result_count: 2 })

2. Add the reverse proxy route

Create app/routes/ph.$.ts:

import { createPostHogProxy } from "@activescott/analytics/proxy"

export const { loader, action } = createPostHogProxy({
  apiHost: "https://us.i.posthog.com",
  assetsHost: "https://us-assets.i.posthog.com",
  // Opt in only if your privacy policy covers it: forwards the ingress-set
  // X-Forwarded-For chain so PostHog sees client IPs for geoIP.
  forwardIp: true,
})

Register the route in app/routes.ts:

route("ph/*", "routes/ph.$.ts"),

The proxy hardens the relay for you: upstream origins are fixed at configuration time, Cookie/Authorization (and everything but content-type + user-agent) are stripped, Set-Cookie is stripped from responses, request bodies are capped (default 1 MiB), the upstream call has a timeout (default 10s), and only known PostHog path prefixes (e, batch, decide, flags, s, surveys, array, static) are proxied. IP forwarding is deliberately absent — X-Forwarded-For passthrough vs ?ip= is a privacy decision for you to make explicitly.

That's it! PostHog will now:

  • Track pageviews on every client-side navigation
  • Identify logged-in users by id
  • Receive events through your own domain (/ph/*)

Deriving sibling origins

@activescott/analytics/hosts exports the pure helpers posthogAssetsHost and posthogUiHost, which map a PostHog API origin to its assets and app-UI siblings (<region>.i.posthog.com → <region>-assets.i.posthog.com / <region>.posthog.com; anything else passes through unchanged) — so a deployment carries one host knob instead of three.

Versioning

Independent plain-npm versions with git tags of the form analytics@<version> (see repo README). Consumers pin the versioned package normally — lockfile, Dependabot, and Docker all work unchanged.