DavidWells/analytics: A Pluggable Abstraction Layer for Page Views, Events and Visitor Identity
Lightweight analytics abstraction layer for tracking page views, custom events, & identifying visitors
At a glance
- What is it?
- The npm package analytics gives JavaScript applications one API for page views, custom events and visitor identification, with third-party tools loaded as plugins. It is a routing layer, not a dashboard, and its value depends on how many providers you swap over time.
- Who is it for?
- Adopt DavidWells/analytics if you expect to change analytics vendors, need the same tracking calls in the browser and on the server, or want plugin lifecycle hooks around every tracking call. Do not adopt it if you need a reporting UI or a warehouse; it forwards events and keeps no dashboard.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 98 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The vendor-swap problem DavidWells/analytics targets
Analytics requirements change. A company adds a product analytics tool, drops one after a procurement review, or adds a marketing pixel for a campaign. In most codebases each of those changes touches every call site, because the tracking call is written against a specific vendor's SDK. The README states the motivation directly: companies frequently change analytics requirements based on evolving needs, which produces complexity, maintenance work and extra code when services are added or removed.
The library's answer is to make the provider list configuration rather than code. You call analytics.page(), analytics.track() and analytics.identify() once, and the plugins you pass at initialization decide where those calls go. Removing a tool means deleting an entry from the plugins array. The intended audience is application and frontend engineers who own tracking code and are tired of rewriting it per vendor, plus teams that want the same tracking calls to work in a browser bundle and in a Node process.
How the plugin layer routes calls and buffers them
The repository is a pnpm and lerna monorepo. The core package is analytics, which wraps @analytics/core and analytics-utils, and the provider integrations live as separate packages under packages/. That split matters: the core does not bundle a vendor SDK, so the size you ship depends on which plugins you import.
Initialization takes an object with app, version and plugins. Each plugin is a function that receives its own configuration and returns the methods the core will call. The README describes the core API as exposed once the library is initialized, and lists lifecycle hooks, which are the mechanism for adding functionality or modifying tracking calls. The feature list also states that events are queued to send when analytic libraries are loaded, and that third-party scripts can be loaded conditionally. That queue is what lets a page call analytics.page() before a vendor script has finished downloading.
Two capabilities shape how you test. The README advertises time travel and offline mode for testing and debugging integrations, and a debugging section in the table of contents. The library is isomorphic, so the same calls run in the browser and on the server; the README shows a separate Node.js import path for CommonJS.
Installing analytics and sending your first event
The package is distributed on npm and is meant to be a project dependency. The README gives install commands for npm, yarn, pnpm and bun; the npm form is below.
npm install analytics --saveAfter installing, import the core and at least one provider plugin. The README's usage example pairs the core with @analytics/google-analytics and @analytics/customerio, and configures them with measurementIds and siteId respectively. Those plugin packages are separate installs; the snippet below shows the shape of the initialization and the three call types.
import Analytics from 'analytics'
import googleAnalytics from '@analytics/google-analytics'
import customerIo from '@analytics/customerio'
const analytics = Analytics({
app: 'my-app-name',
version: 100,
plugins: [
googleAnalytics({
measurementIds: ['G-XXXXXXXX'],
}),
customerIo({
siteId: '123-xyz'
})
]
})
analytics.page()A page view is analytics.page(). A custom event is analytics.track() with an event name and a payload object, for example userPurchase with price and item. A visitor is identified with analytics.identify(), which takes a user id and a traits object. The README also lists analytics.user, analytics.reset, analytics.ready, analytics.on and analytics.once for reading state and reacting to events, plus analytics.getState and a storage interface with getItem, setItem and removeItem.
If you want to try it without a bundler, the README offers a script tag pointing at the unpkg build. In that mode the library exposes a global _analytics variable, and you create an instance with _analytics.init() using the same configuration object.
Where the abstraction stops being the right tool
This is a routing and buffering layer. It does not store events, query them, or render charts. If your actual need is a dashboard or a warehouse with SQL access, you are looking at the wrong package; you would still need a backend or a provider behind it.
There is a second, sharper limitation. The README's philosophy says respecting visitor privacy settings and allowing opt-out mechanisms is important, and the repository topics include gdpr-compliant. But the README does not describe a built-in consent manager that blocks calls before they fire. Consent handling appears to be the responsibility of the plugin or of your own code around the lifecycle hooks. If you need a formal consent gate with an audit trail, that is something you must build or verify plugin by plugin.
A third issue is plugin coverage. The value of a provider-agnostic layer is proportional to the number of maintained provider plugins. The README links a plugin directory and a community plugins section, and the repository root carries an external-plugins.json file, which suggests a registry of plugins outside the monorepo. Nothing in the README states that community plugins are reviewed or kept in step with vendor API changes. A plugin written against an older version of a vendor SDK is your maintenance problem, not the core's.
How this differs from Segment and from a single-vendor SDK
Segment solves the same problem from the other direction. It is a hosted collection endpoint: your application sends events to Segment's servers, and Segment forwards them to destinations configured in its own dashboard. Changing a destination is a dashboard change, and the vendor sees your event stream.
DavidWells/analytics keeps the fan-out in your bundle. Plugins call provider SDKs directly from the client or server where the code runs, and there is no intermediary service in the path. That means no per-event cost and no third party holding the raw stream, but it also means configuration lives in your code and deploys, not in an operator UI. You also carry the plugin code in your bundle.
Against a single-vendor SDK, the difference is narrower but real. If you will only ever use one analytics tool, the vendor's own SDK is simpler and better documented for that tool. The abstraction earns its place when the provider list is expected to change, or when the same tracking calls must run in more than one runtime.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-06-27. That is roughly three months before the date of this review, so recent commits exist, but the README does not describe a release cadence or a support commitment. The README lists a Support section and points to documentation at getanalytics.io.
The code is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a statement about the licence text, not legal advice; if you redistribute a modified core or plugin, check how you attribute it.
Upgrade cost concentrates in the plugins, not the core. The core API surface is small and stable in shape: configuration plus page, track, identify and the state helpers. Plugin packages track vendor SDK changes and are versioned separately in the monorepo. A vendor that renames a configuration key, as the README's measurementIds example shows for Google Analytics, forces a change in the plugin and possibly in your initialization object. Budget for reading plugin changelogs when you upgrade, and pin plugin versions rather than floating them.
Editorial conclusion
Adopt DavidWells/analytics if you expect to change analytics vendors, need the same tracking calls in the browser and on the server, or want plugin lifecycle hooks around every tracking call. Do not adopt it if you need a reporting UI or a warehouse; it forwards events and keeps no dashboard. Before committing, verify that every provider you depend on has a maintained plugin, and check whether your privacy requirements can be met by the plugin-level consent handling rather than by a built-in consent manager.
Frequently asked questions
How do I install the analytics package?
Install it as a project dependency with npm install analytics --save, or the equivalent yarn, pnpm or bun command from the README. Provider integrations such as @analytics/google-analytics are separate packages you install alongside it.
How do I use analytics to track a page view or a custom event?
Initialize the library with an app name, a version and a plugins array, then call analytics.page() for a page view or analytics.track() with an event name and payload object for a custom event. The README's example uses analytics.track('userPurchase', { price: 20, item: 'pink socks' }).
Does DavidWells/analytics include a consent manager for GDPR?
The README states that respecting visitor privacy settings and allowing opt-out mechanisms is part of the driving philosophy, and the repository is tagged gdpr-compliant. The README does not describe a built-in consent gate that blocks calls before they fire, so consent handling falls to your code or to individual plugins.
Can I use DavidWells/analytics on the server as well as in the browser?
The README lists the library as isomorphic and shows a separate Node.js usage example using require('analytics') alongside the browser import. The same page, track and identify calls are shown in both examples.
What happens if analytics.page() runs before the third-party script has loaded?
The feature list states that the library queues events to send when analytic libraries are loaded, and that it can conditionally load third-party scripts. That buffering is what allows calls to be made before a vendor SDK is ready.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/davidwells-analytics)