ahoy.js

Lightweight JavaScript library for tracking website visits and events with any backend.

Library
npm
v0.4.5
508stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
54/100Fair
Development Activity40
Maintenance20
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
46/100Fair
Architecture65
Code Quality35
Innovation40
Learning Curve45

Ahoy.js is a small, dependency-free JavaScript library for first-party analytics. It assigns visit and visitor cookies on page load, tracks page views, clicks, and form submits, and posts the data to endpoints your own backend controls — there’s no hosted service or SaaS dashboard in the loop.

It pairs naturally with the Ahoy gem for Rails apps, but the JSON payloads it sends (visit_token, visitor_token, referrer, landing_page for visits; name, properties, time for events) are backend-agnostic, so it works with any server willing to accept and store them. The library ships as UMD and ESM builds via Rollup and has no runtime dependencies of its own.

What You Get

  • Automatic visit/visitor cookie management with configurable expirations (4 hours for visits, 2 years for visitors by default)
  • A track(name, properties) API plus convenience wrappers for page views, link clicks, and form submits
  • Delivery via navigator.sendBeacon when available, falling back to jQuery/Zepto $.ajax or a raw XMLHttpRequest
  • An event queue persisted in a cookie so events survive page navigation and are retried until the server acknowledges them
  • CSRF token auto-detection from <meta name="csrf-token">/<meta name="csrf-param"> tags for same-origin Rails-style backends
  • A debug() mode that logs visit/event payloads to the console for local development

Common Use Cases

  • Adding first-party visit and pageview analytics to a server-rendered app without depending on a third-party analytics vendor
  • Tracking link clicks and form submissions site-wide with a single trackClicks/trackSubmits selector call
  • Recording custom product events (ahoy.track('Signed up', {...})) tied to an authenticated user on the server
  • Cross-subdomain visit tracking by sharing a cookie domain across multiple properties
  • Attaching visit/visitor identifiers to API requests made via fetch when cookies can’t cross origins

Under The Hood

Architecture Ahoy.js is a single flat closure-style module (src/index.js, under 500 lines) attached to a shared window.ahoy object, with cookie get/set/delete logic isolated in a tiny src/cookies.js helper. Execution starts on documentReady, which calls ahoy.start() to run createVisit() — reading or minting visit/visitor cookies and POSTing a visit payload — after which a isReady flag and an internal callback queue (ahoy.ready) gate any track() calls issued before the visit handshake completes. Events queued by ahoy.track() are also persisted into an ahoy_events cookie via saveEventQueue() so they survive a page unload and are retried on the next load. There’s no dependency injection or plugin system; every function reads and mutates the same closure-scoped config object directly, so changing that object’s shape would touch nearly every function in the file.

Tech Stack The library has zero runtime dependencies — it’s built entirely on browser primitives (document.cookie, XMLHttpRequest, navigator.sendBeacon, FormData) with an optional runtime check for jQuery/Zepto’s $.ajax if present on the page. The build pipeline is Rollup 4.x with @rollup/plugin-buble (ES5 transpilation) and @rollup/plugin-terser (minification), producing three artifacts from one src/index.js entry point: a UMD bundle (dist/ahoy.js), a minified UMD bundle (dist/ahoy.min.js), and an ES module bundle (dist/ahoy.esm.js), wired up through package.json’s exports map. CI (GitHub Actions) runs npm install && npm run build only — it verifies the bundle compiles, not that it behaves correctly.

Code Quality There are no test files anywhere in the repository and no linter or formatter configuration, so code quality rests entirely on manual review and the maintainer’s own usage in the companion Ahoy Rails gem. Error handling is sparse: the only try/catch guards JSON.parse of the cookie-stored event queue on load, while public methods like trackClicks/trackSubmits throw a plain Error if called without a required selector argument. Naming is consistent camelCase throughout, and defensive checks (typeof, Object.prototype.hasOwnProperty.call) are used in place of a type system, since the project ships no TypeScript definitions.

What Makes It Unique Ahoy.js doesn’t introduce a novel tracking model — cookie-based visit/visitor identity plus a retry-on-cookie event queue is a well-established pattern for lightweight, self-hosted analytics. Its distinguishing choice is the fallback chain for delivery (sendBeacon first, then jQuery’s $.ajax, then a manual XMLHttpRequest) combined with persisting the in-flight event queue in a cookie rather than memory, which lets events queued right before a navigation still reach the server on the next page load.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search