Plausible client to collect analytics in browser with no hassle.
There are official plausible-tracker package, it have quite few code, but have a lot of bugs like
- "enableAutoOutboundTracking breaks target="_blank" and probably noopener security" #12
- uses XHR API that loses analytics on static sites with no SPA approach (like Astro does) #16
- uses callbacks, that will not be called on localhost, that force users write code that works different locally and on production
Current package is lightweight too, but written and maintained by those who use a plausible analytics on production. See dogfooding.
Install package with npm i plausible-client
Create instance and play
import { Plausible } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
plausible.sendEvent('test', {
props: {
foo: 1,
bar: 'string',
baz: null,
},
revenue: {
currency: 'USD',
amount: 5,
},
});To track page views automatically, call enableAutoPageviews:
import { Plausible, enableAutoPageviews } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
// Function returns cleanup callback and starts track pageviews
enableAutoPageviews(plausible);To track outbound clicks automatically (same-origin links are skipped automatically), call enableAutoOutboundTracking:
import { Plausible, enableAutoOutboundTracking } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
// Function returns cleanup callback and starts tracking outbound clicks
enableAutoOutboundTracking(plausible);
// Optionally capture link text or apply a custom filter
enableAutoOutboundTracking(plausible, {
captureText: true,
filter: (url, text) => !url.includes('internal'),
});For fine-grained control over which links to track, use enableLinkClicksCapture directly:
import { Plausible, enableLinkClicksCapture } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
// Function returns cleanup callback
enableLinkClicksCapture(plausible, {
eventName: 'Link click', // default
captureText: true, // include link text as a prop
filter: (url, text) => url.startsWith('https://'),
});To collect device/region signals and detect trivial bots, call enableSessionScoring:
import { Plausible, enableSessionScoring } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
// Sends a "Session scored" event on the next animation frame
enableSessionScoring(plausible);The Session scored event includes these props:
| Prop | Description |
|---|---|
botScore |
Numeric score — ≥ 3 indicates a likely bot |
botSignals |
Comma-separated triggered signals, e.g. webdriver,headless_ua |
sessionAge |
Seconds since the first recorded visit |
timeZone |
IANA timezone resolved from Intl.DateTimeFormat |
language |
navigator.language |
languages |
navigator.languages joined with , |
screenSize |
{width}x{height} from window.screen |
hardwareConcurrency |
Number of logical CPU cores |
deviceMemory |
Device RAM in GB (where available) |
devicePixelRatio |
Screen pixel density |
You can customise the storage backend or key used to persist the first-visit timestamp:
import { Plausible, enableSessionScoring, CookieStorage } from 'plausible-client';
enableSessionScoring(plausible, {
storage: new CookieStorage(),
firstVisitKey: 'my_first_visit',
});You can also use getBotSignals() independently:
import { getBotSignals } from 'plausible-client';
const { isBot, score, signals } = getBotSignals();
// isBot: boolean, score: number, signals: string[]To automatically track user engagement — scroll depth and active time on the page — call enableEngagementTracking:
import { Plausible, enableEngagementTracking } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
});
// Function returns a cleanup callback and starts tracking engagement
enableEngagementTracking(plausible);The tracker sends a Page engagement event to Plausible. Tracking is only active while the browser tab is visible and the window is focused — time spent on a hidden or unfocused tab is excluded.
Events are debounced (emitted at most once every 3 seconds) and triggered by scroll activity and tab visibility changes.
The Page engagement event includes these props:
| Prop | Description |
|---|---|
scrollDepth |
Furthest scroll position reached on the page, as a percentage (0–100) |
timeOnPage |
Seconds the user has actively spent on the page (tab visible + focused) |
For custom integrations, you can use the lower-level EngagementTimeTracker class independently:
import { EngagementTimeTracker } from 'plausible-client';
const tracker = new EngagementTimeTracker();
tracker.start();
// Check whether the document is currently active
console.log(tracker.isActive()); // true / false
// Get total active milliseconds accumulated so far
console.log(tracker.getTotalTime());
// Subscribe to visibility changes
const unsubscribe = tracker.onVisibilityChanged((isActive) => {
console.log('Active:', isActive);
});
// Tear down when done
unsubscribe();
tracker.stop();You may filter out specific events.
It may be useful to skip events of users who should not be tracked, ignore irrelevant events by its props, and for development purposes.
Just define predicate filter in config:
- it receive event object as first parameter and event name as second
- it must return
trueto send request andfalseto skip
import { Plausible } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
filter(event, eventName) {
// Skip all events while development
if (location.hostname === 'localhost') {
console.warn('Analytics event is skipped, since run on localhost', event);
return false;
}
// Skip all events for users who don't want to be tracked
if (window.localStorage.plausible_ignore === 'true') return false;
// Skip events by event props
if (event.props.group === 'no-track') return false;
// Skip events by its name, for users who does not participate in preview program
if (!event.props.previewEnabled && eventName.startsWith('preview:')) return false;
// Pass all events otherwise
return true;
}
});You may use default filters
import { Plausible, filters, skipByFlag, skipForHosts } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
// Compose many filters via `filters` call
filter: filters(
// Ignore events if flag is set as 'true' in provided storage
skipByFlag('plausible_ignore', localStorage),
// Ignore events sent from listed hostnames
skipForHosts(['localhost'])
)
});You may transform events.
It may be useful to enrich events data or redact some collected data.
Just define option transform in config
- it receive event object and event name
- it must return new event object.
import { Plausible } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
transform(event, eventName) {
event.props = {
...event.props,
group: 'clients',
userId: event.props.uid ? "uuid:" + event.props.uid : undefined,
isPreferDarkTheme: window.matchMedia("(prefers-color-scheme: dark)").matches,
};
return event;
}
});Transformation hook runs after filter.
You may automatically inject user id to all events via default transformer:
import { Plausible, userId } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
transform: userId(),
});If no config provided to a transformer, user ID will be persist in localStorage with key plausible_uid.
You may customize how user ID is stored via storage option. You may pass any implementation of Storage like localStorage or sessionStorage.
Also you may pass CookieStorage that implements Storage interface.
import { Plausible, userId, BrowserUIDStorage, CookieStorage } from 'plausible-client';
const plausible = new Plausible({
apiHost: 'https://plausible.io',
domain: 'example.org',
// User ID will be persist in localStorage with key `plausible_uid`
transform: userId({
storage: new BrowserUIDStorage({
// Store UID in JS cookies
store: CookieStorage(),
// Customize storage key to persist ID
key: 'uid'
}),
}),
});plausible-client is an truth open source project, so you are welcome on project github repository to contribute a code, make issues with feature requests and bug reports.
You may contribute to a project if you tell about plausible-client to your friends.