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

@pakicetus97/next-analytics

v0.2.1

Published

App Router-only analytics provider for Next.js that posts events to a configurable collect endpoint.

Downloads

112

Readme

@pakicetus97/next-analytics

App Router-only analytics package for Next.js. It sends client-side analytics events to a configurable collect endpoint that you implement in the consumer project.

Installation

pnpm add @pakicetus97/next-analytics

Usage

import { AnalyticsProvider } from '@pakicetus97/next-analytics'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="de">
      <body>
        <AnalyticsProvider endpoint="/api/collect" />
        {children}
      </body>
    </html>
  )
}

The endpoint can be a relative path inside the consumer app or a full URL:

<AnalyticsProvider endpoint="https://analytics.example.com/collect" />

Props

| Prop | Type | Default | Description | |---|---|---|---| | endpoint | string | — | URL to POST events to | | publicKey | string | — | Sent as X-Public-Key header on every request | | sessionStorageKey | string | "analytics_session_id" | Key used to persist the session ID | | batch | boolean \| BatchOptions | — | Queue events and flush in batches | | retry | boolean \| RetryOptions | — | Retry failed requests with exponential backoff | | throttle | boolean \| ThrottleOptions | — | Limit how often requests are sent |

batch

// defaults: maxSize 10, flushIntervalMs 5000
<AnalyticsProvider endpoint="/api/collect" batch />

// custom
<AnalyticsProvider endpoint="/api/collect" batch={{ maxSize: 20, flushIntervalMs: 10000 }} />

Events are collected in a queue and sent together when either maxSize is reached or flushIntervalMs has elapsed. The queue is always force-flushed on page unload, route change, and when the tab becomes hidden.

retry

// defaults: attempts 3, delayMs 500 (exponential: 500ms → 1000ms → 2000ms)
<AnalyticsProvider endpoint="/api/collect" retry />

// custom
<AnalyticsProvider endpoint="/api/collect" retry={{ attempts: 5, delayMs: 300 }} />

Only retries on network errors, not on HTTP error status codes. Retry is skipped for requests that are sent on page unload (no time to wait).

throttle

// default: minIntervalMs 1000
<AnalyticsProvider endpoint="/api/collect" throttle />

// custom
<AnalyticsProvider endpoint="/api/collect" throttle={{ minIntervalMs: 2000 }} />

Drops events that arrive within minIntervalMs of the last send. When batch is enabled, throttle is ignored — the flush interval already controls request rate.

Combined example

<AnalyticsProvider
  endpoint="/api/collect"
  publicKey="pk_live_abc123"
  batch={{ flushIntervalMs: 3000 }}
  retry
/>

Implementing the Collect Endpoint

This package does not include a /api/collect handler. You implement it yourself in your consumer project. Here is everything you need to know.

HTTP Request

POST {endpoint}
Content-Type: application/json
X-Public-Key: <your-key>   // only present when publicKey prop is set

The body is either a single event object or an array of event objects (when batch is enabled). Your handler must accept both.

The endpoint can live inside the same Next.js project (e.g. app/api/collect/route.ts) or be a completely separate service — as long as it accepts POST requests with a JSON body.

The client ignores the response body — returning 200 or 204 is enough. Error status codes are not retried automatically (only network-level errors are, when retry is configured).

Security recommendations

  • Validate the X-Public-Key header if you use the publicKey prop. Reject requests with a missing or invalid key with 401.
  • Validate the Content-Type header and reject anything that is not application/json.
  • Validate the event shape before persisting — check that type, timestamp, and sessionId are present and have the expected types. Reject or discard malformed events.
  • Rate-limit by IP or session to prevent abuse. Since events can arrive in bursts (e.g. on fast navigation), allow short bursts but cap sustained throughput.
  • Do not trust sessionId as an authentication token — it is generated client-side and can be spoofed. Treat it as an opaque correlation ID only.
  • Never reflect raw event data back to any client without sanitisation, as it originates from the browser.

Event Shapes

All events share these base fields:

| Field | Type | Description | |---|---|---| | type | string | Event type identifier | | timestamp | string | ISO 8601 UTC (new Date().toISOString()) | | sessionId | string | Unique per browser-tab session, stored in sessionStorage | | path | string | Current pathname (e.g. /dashboard) |


page_view

Fired on every client-side route change.

{
  "type": "page_view",
  "timestamp": "2026-04-08T12:00:00.000Z",
  "sessionId": "abc123",
  "path": "/dashboard",
  "referrer": "google.com",
  "device": "desktop"
}

| Field | Type | Notes | |---|---|---| | referrer | string \| undefined | Hostname only, www. stripped. Omitted if no referrer. | | device | "mobile" \| "tablet" \| "desktop" | Detected from navigator.userAgent |


scroll

Fired when the user leaves a page (route change or pagehide). Reports the furthest scroll position reached.

{
  "type": "scroll",
  "timestamp": "2026-04-08T12:00:00.000Z",
  "sessionId": "abc123",
  "path": "/dashboard",
  "maxDepth": 75
}

| Field | Type | Notes | |---|---|---| | maxDepth | number | Integer 0–100 (percentage of page height scrolled) |


web_vitals

Fired on navigation or when the tab becomes hidden. Only sent when at least one metric was collected.

{
  "type": "web_vitals",
  "timestamp": "2026-04-08T12:00:00.000Z",
  "sessionId": "abc123",
  "path": "/dashboard",
  "metrics": {
    "CLS": 0.05,
    "FCP": 1200,
    "LCP": 2400,
    "INP": 80,
    "TTFB": 300
  }
}

| Metric | Unit | Description | |---|---|---| | CLS | score (0–1) | Cumulative Layout Shift | | FCP | ms | First Contentful Paint | | FID | ms | First Input Delay | | INP | ms | Interaction to Next Paint | | LCP | ms | Largest Contentful Paint | | TTFB | ms | Time to First Byte |

All metrics fields are optional — only the ones measured in the current session are included.


anchor_navigation

Fired when the URL hash changes (e.g. clicking an in-page anchor link).

{
  "type": "anchor_navigation",
  "timestamp": "2026-04-08T12:00:00.000Z",
  "sessionId": "abc123",
  "path": "/docs",
  "anchor": "#getting-started"
}

| Field | Type | Notes | |---|---|---| | anchor | string | The full hash including # |


Batch mode

When batch is enabled, multiple events are sent as a JSON array in a single request:

[
  { "type": "page_view", "timestamp": "...", "sessionId": "...", "path": "/", "device": "desktop" },
  { "type": "scroll",    "timestamp": "...", "sessionId": "...", "path": "/", "maxDepth": 42 }
]

Your handler must handle both {} and [{}, {}] — see the example above.

Notes

  • Next.js App Router only.
  • No /api/collect handler is included in this package.
  • The consumer project must implement the collect endpoint itself.

Development

pnpm install
pnpm typecheck
pnpm build
pnpm pack

Detailed publish steps are documented in PUBLISHING.md.