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

unique-visitor-tracker

v1.0.0

Published

Lightweight, privacy-first unique visitor and event tracking helper for web apps (browser + Node middleware)

Readme

unique-visitor-tracker

Lightweight, privacy-first unique visitor & event tracking helper (browser) + tiny Express receiver (server).

  • No dependencies
  • Anonymous IDs only (no cookies required, but supported)
  • Works anywhere: vanilla JS, React, Vue, Svelte…
  • Server optional: if you set an endpoint, events are POSTed; otherwise you can intercept payloads in your app.

Install

npm i unique-visitor-tracker
# or
pnpm add unique-visitor-tracker

Quick start (browser)

import VisitorTracker from 'unique-visitor-tracker';

const tracker = new VisitorTracker({
  endpoint: '/api/track',      // optional: POST destination
  storage: 'auto',             // 'auto' | 'localStorage' | 'cookie' | 'memory'
  meta: { projectId: 'demo' }  // merged into every event
});

console.log('visitor id', tracker.getId());

// page view
tracker.trackPage();

// custom event
tracker.trackEvent('button_click', { label: 'Buy now' });

Single-call helper

import { trackVisit } from 'unique-visitor-tracker';
trackVisit('/api/track');

Server (Express) — with analytics

import express from 'express';
import { createReceiver, createAnalytics } from 'unique-visitor-tracker/middleware/express';

const app = express();
app.use(express.json());

// naive in-memory store
const events = [];
const storage = { getAll: () => events };
const analytics = createAnalytics(storage);

// Track events
app.post('/api/track', createReceiver({
  save: (evt) => events.push(evt),
  // allow: (req) => req.headers['x-api-key'] === process.env.API_KEY
}));

// Analytics endpoints
app.get('/api/analytics/visitors/count', async (req, res) => {
  const count = await analytics.getUniqueVisitorCount();
  res.json({ uniqueVisitors: count });
});

app.get('/api/analytics/visitor/:id', async (req, res) => {
  const info = await analytics.getVisitorInfo(req.params.id);
  res.json(info);
});

app.listen(3000, () => console.log('listening on http://localhost:3000'));

API

new VisitorTracker(options)

  • endpoint?: string – POST destination for events (optional).
  • storage?: 'auto' | 'localStorage' | 'cookie' | 'memory' – where to persist the visitor id (default: auto).
  • meta?: object – merged into every event.

Methods

  • getId(): string – returns the unique visitor id.
  • trackPage(path?: string, data?: object): Promise<SendResult> – track a page view.
  • trackEvent(name: string, data?: object): Promise<SendResult> – track a custom event.
  • resetId(): string – clears and regenerates the visitor id.

Helpers

  • getVisitorId({ storage }?): string
  • trackVisit(endpoint, path?, meta?, data?): Promise<SendResult>

Server

  • createReceiver({ save, allow? }) – returns an Express-compatible handler that validates, annotates and stores events via your save function. IPs are hashed before saving.
  • createAnalytics(storage) – returns analytics functions for counting unique visitors and getting visitor information.

Analytics

import { createAnalytics } from 'unique-visitor-tracker/middleware/express';

const analytics = createAnalytics(storage);

// Count unique visitors
const count = await analytics.getUniqueVisitorCount();

// Get detailed info about a specific visitor
const visitorInfo = await analytics.getVisitorInfo('uvt_abc123');
// Returns: { visitorId, firstSeen, lastSeen, totalEvents, client, events }

Event payload shape

{
  "type": "page_view",
  "ts": "2025-09-07T00:00:00.000Z",
  "visitorId": "uvt_d3AL4...",
  "client": {
    "env": "browser",
    "tz": "Asia/Dhaka",
    "lang": "en-US",
    "ua": "Mozilla/5.0 ...",
    "screen": { "w": 1920, "h": 1080, "dpr": 1 }
  },
  "projectId": "demo", // from meta
  "data": { "path": "/" }
}

Privacy

  • No personal data collected by default.
  • No IP stored; a non-reversible hash of the source IP is attached as ipHash on the server.
  • Compliant with common privacy regimes when used properly. Always consult your legal team for your specific use-case.

Complete Feature Set

This package provides everything you need for visitor tracking and analytics:

✅ Unique Visitor Tracking

  • Automatic ID generation with uvt_ prefix
  • Persistent storage across sessions (localStorage, cookies, or memory)
  • Privacy-first approach with anonymous IDs only

✅ Event Tracking

  • Page views with automatic path detection
  • Custom events with flexible data payloads
  • Client context (timezone, language, screen size, user agent)

✅ Analytics & Insights

  • Count unique visitors across all time
  • Get detailed visitor information including first/last seen, total events, and full event history
  • Event aggregation and analysis capabilities

✅ Flexible Storage

  • Multiple storage backends supported (see examples/storage-adapters.js)
  • Database agnostic - works with MongoDB, PostgreSQL, Redis, SQLite, or simple files
  • Custom storage adapters easy to implement

✅ Privacy & Security

  • No personal data collected by default
  • IP address hashing for privacy compliance
  • Configurable data retention and opt-out flows

Examples

See the examples/ directory for complete usage examples:

  • complete-server-example.js - Full server with analytics dashboard
  • client-usage.js - Comprehensive client-side usage
  • storage-adapters.js - Database integration examples
  • react-usage.jsx - React integration

Tips

  • In SPAs, call trackPage() inside your router listener.
  • For downloads, trackEvent('download', { file: 'report.pdf' }).
  • For UTM analytics, merge location.search into data.
  • Use the analytics functions to build dashboards and insights.
  • Implement custom storage adapters for your preferred database.

License

MIT


---

## tests/smoke.js
```js
import VisitorTracker from '../src/index.js';

const tracker = new VisitorTracker({ endpoint: null, storage: 'memory' });
const id1 = tracker.getId();
const id2 = tracker.getId();
if (id1 !== id2) throw new Error('ID not stable across calls');

await tracker.trackPage('/');
await tracker.trackEvent('demo', { ok: true });

console.log('smoke OK', { id: id1 });

examples/react-usage.jsx

import React, { useEffect } from 'react';
import VisitorTracker from 'unique-visitor-tracker';

const tracker = new VisitorTracker({ endpoint: '/api/track', meta: { app: 'example' } });

export default function App() {
  useEffect(() => {
    tracker.trackPage(window.location.pathname);
  }, []);

  return (
    <button onClick={() => tracker.trackEvent('cta_click', { where: 'hero' })}>
      Click me
    </button>
  );
}