@indiebob/tracker

Privacy-first analytics SDK for web applications. <5KB gzipped. No cookies.
1,131
Install
npm install @indiebob/tracker
Latest Version:0.2.4
Vue:3
License:MIT
Last Updated:Sep 9, 2026
Links: GitLab  ·  npm
Maintainer: priit.parna

@indiebob/tracker

Privacy-first analytics SDK for web applications. Under 5KB gzipped. No cookies. GDPR/CCPA/GPC compliant.

Installation

npm install @indiebob/tracker
# or
pnpm add @indiebob/tracker

Or via script tag (CDN):

<script src="https://assets.indiebob.com/tracker/latest/indiebob.min.js"></script>

Quick Start

Three lines to start tracking:

import { IndieBob } from '@indiebob/tracker'

const bob = IndieBob.init({ projectId: 'proj_your16charId' })
bob.page()

That's it. Page views are now tracked. UTM parameters are auto-captured.

Configuration

const bob = IndieBob.init({
  projectId: 'proj_your16charId',  // Required. From your IndieBob dashboard.
  endpoint: 'https://indiebob.com/api/collect',  // Optional. Default shown.
  debug: false,           // Optional. Logs events to console when true.
  respectDnt: true,       // Optional. Honor browser Do Not Track. Default: true.
  respectGpc: true,       // Optional. Honor Global Privacy Control. Default: true.
  autoDetect: true,       // Optional. Auto-detect signups, logins, checkouts, etc. Default: true.
})
Option Type Default Description
projectId string required Your SDK project ID from the dashboard
endpoint string 'https://indiebob.com/api/collect' Override for self-hosted instances
debug boolean false Log all events to browser console
respectDnt boolean true Honor the browser's Do Not Track header
respectGpc boolean true Honor Global Privacy Control signal (legally binding in CA/GDPR)
autoDetect boolean | object true Auto-detect user intent from URL patterns, form submissions, outbound clicks, and data attributes. See Auto-Detection.

API Reference

IndieBob.init(config)

Creates and returns a tracker instance. Call once at app startup.

const bob = IndieBob.init({ projectId: 'proj_abc123def456ghij' })

bob.page(overrides?)

Track a page view. Call this on every page load and SPA navigation.

bob.page()                           // Auto-detects path, title, referrer
bob.page({ path: '/pricing' })       // Override path
bob.page({ title: 'Pricing Page' }) // Override title

Auto-detected: URL path, page title, referrer, UTM parameters, device type.


bob.track(event, properties?)

Track a custom event.

bob.track('signup_started')
bob.track('trial_activated', { plan: 'pro' })
bob.track('feature_used', { feature: 'csv_export', count: 3 })
bob.track('checkout_completed', { revenue: 29.00, currency: 'USD', plan: 'pro-monthly' })

event — string, required. Max 256 characters. Use snake_case by convention.

properties — plain object, optional. Values must be JSON-serializable.


bob.identify(userId, traits?)

Associate the current anonymous visitor with a known user ID. Call this after login or signup.

bob.identify('user_123', {
  email: 'jane@example.com',
  name: 'Jane Smith',
  plan: 'pro',
  createdAt: '2026-01-15',
})

userId — string, required. Your internal user ID. Never pass PII as the userId directly.

traits — plain object, optional. Can include email for attribution linking.

After identify(), all subsequent events are linked to this userId until the session ends.


bob.revenue(data)

Track a revenue event. Use alongside Stripe webhooks for complete attribution.

bob.revenue({
  amount: 29.00,           // Required. Positive number.
  currency: 'USD',         // Optional. Default: 'USD'. ISO 4217 code.
  plan: 'pro-monthly',     // Optional. Your plan name.
  type: 'new',             // Optional. 'new' | 'renewal' | 'upgrade' | 'downgrade'
})

Privacy Controls

bob.optOut()       // Stop all tracking. Clears all stored identifiers.
bob.optIn()        // Re-enable tracking.
bob.isOptedOut()   // Returns boolean.

Debug / Testing

bob.getAnonymousId()  // Returns the current anonymous ID
bob.getSessionId()    // Returns the current session ID
bob.getConfig()       // Returns a read-only copy of the config
bob.flush()           // Force-send all queued events immediately

Auto-Detection

The SDK automatically fires semantic events out of the box — no manual bob.track() calls needed for common user journeys. This powers IndieBob's attribution funnels (visit → signup → checkout → paid).

URL Intent Detection

When bob.page() is called, the SDK checks the URL path against known patterns (multilingual: EN, ES, FR, DE, PT, IT, NL) and fires the corresponding event:

Event Triggered By
signup_start /signup, /register, /join, /registro, /inscription, /anmeldung, ...
login_view /login, /sign-in, /iniciar-sesion, /connexion, /anmelden, ...
pricing_view /pricing, /plans, /precios, /tarifs, /preise, ...
checkout_view /checkout, /cart, /basket, /panier, /warenkorb, /kasse, ...
success_view /success, /thank-you, /confirmation, /danke, /merci, ...
onboarding_view /onboarding, /welcome, /getting-started, /bienvenido, ...
contact_view /contact, /contacto, /kontakt, /nous-contacter, ...

Signup vs Login Detection

When bob.identify() is called:

  • First call for this visitor fires signup (new user)
  • Subsequent calls fire login (returning user)

This uses a localStorage flag — no server round-trip needed.

Form Submit Detection

On signup, login, and checkout pages, form submissions are automatically tracked:

  • signup_submit — form submitted on a signup page
  • login_submit — form submitted on a login page
  • checkout_submit — form submitted on a checkout page

Outbound Click Tracking

Clicks on links pointing to external domains automatically fire outbound_click with { url, hostname } properties.

Data Attribute Tracking

Add data-ib-event to any element for zero-JS custom event tracking:

<button data-ib-event="add_to_cart" data-ib-plan="pro" data-ib-price="29">
  Add to Cart
</button>

This fires track('add_to_cart', { plan: 'pro', price: '29' }) on click. All data-ib-* attributes (except data-ib-event) become event properties.

Customizing Auto-Detection

Override the default URL patterns or disable specific features:

const bob = IndieBob.init({
  projectId: 'proj_abc123',
  autoDetect: {
    // Custom URL patterns — strings or RegExp (override defaults)
    signupPaths: ['/my-signup-page'],         // string: matched via path.includes()
    checkoutPaths: [/\/buy|\/purchase/i],     // RegExp: matched via .test()

    // Disable specific features
    outboundClicks: false,
    formSubmit: false,
    dataAttributes: false,
  },
})

Set autoDetect: false to disable all auto-detection entirely.

Option Type Default Description
signupPaths (string | RegExp)[] built-in i18n URL patterns for signup pages
loginPaths (string | RegExp)[] built-in i18n URL patterns for login pages
pricingPaths (string | RegExp)[] built-in i18n URL patterns for pricing pages
checkoutPaths (string | RegExp)[] built-in i18n URL patterns for checkout/cart pages
successPaths (string | RegExp)[] built-in i18n URL patterns for success/thank-you pages
onboardingPaths (string | RegExp)[] built-in i18n URL patterns for onboarding pages
contactPaths (string | RegExp)[] built-in i18n URL patterns for contact pages
dataAttributes boolean true Track clicks on data-ib-event elements
formSubmit boolean true Track form submissions on intent pages
outboundClicks boolean true Track clicks on external links

Framework Integrations

Vue / Nuxt

// main.ts or plugins/tracker.client.ts
import { IndieBobPlugin } from '@indiebob/tracker/vue'

app.use(IndieBobPlugin, { projectId: 'proj_abc123def456ghij' })

Then in any component:

import { useTracker } from '@indiebob/tracker/vue'

const bob = useTracker()
bob.track('button_clicked', { button: 'cta_hero' })

Nuxt plugin (plugins/tracker.client.ts):

import { IndieBobPlugin } from '@indiebob/tracker/vue'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(IndieBobPlugin, {
    projectId: useRuntimeConfig().public.trackerProjectId,
  })
})

Page views auto-track on vue-router navigation.

React

import { IndieBob } from '@indiebob/tracker'
import { useEffect } from 'react'

// Initialize once at app root
const bob = IndieBob.init({ projectId: 'proj_abc123def456ghij' })

// In a page component or layout
useEffect(() => {
  bob.page()
}, [pathname])

Vanilla JS (Script Tag)

<script src="https://assets.indiebob.com/tracker/latest/indiebob.min.js"></script>
<script>
  const bob = IndieBob.init({ projectId: 'proj_abc123def456ghij' })
  bob.page()
</script>

Server-Side (Node.js)

import { IndieBob } from '@indiebob/tracker/server'

const bob = IndieBob.initServer({ projectId: 'proj_abc123def456ghij' })
bob.track('server_event', { source: 'cron_job' })

How It Works

Anonymous ID

A random 21-character ID is generated on first visit and stored in localStorage. This persists across sessions so you can track a visitor's journey before they sign up.

  • Stored as _ib_anon in localStorage
  • Falls back to sessionStorage if localStorage is blocked
  • Never uses cookies — no consent banner required
  • Cleared when optOut() is called

Session ID

A session ID is generated per browser tab/window using crypto.randomUUID(). Sessions expire after 30 minutes of inactivity or when the tab closes.

UTM Attribution

UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) are captured from the URL automatically on init().

  • First-touch attribution stored in _ib_first_utm — never overwritten
  • Last-touch attribution in _ib_last_utm — updated each time UTMs are present in the URL
  • Enables "which campaign drove this paying customer?" — the core attribution question

Event Batching

Events are batched for efficiency:

  • Sent when 10 events accumulate
  • Sent every 5 seconds regardless
  • Sent immediately when the page becomes hidden (tab switch, close)
  • Queued in localStorage when offline; flushed when back online

Privacy by Default

The SDK respects:

  • Do Not Track (navigator.doNotTrack === '1'): no events sent, respectDnt: true by default
  • Global Privacy Control (navigator.globalPrivacyControl): legally binding under CCPA in California, respectGpc: true by default
  • Neither cookies nor fingerprinting is used at any point
  • IP addresses are never stored by the server — only the country code derived at ingestion time

What Is and Isn't Collected

Automatically collected (on page())

Data What Stored As
Page path /pricing, /blog/post-1 String
Page title document.title String
Referrer document.referrer String
UTM params From URL query string Object
Screen size window.innerWidth/Height Numbers
Device type Derived from screen size 'desktop' | 'mobile' | 'tablet'
Browser locale navigator.language String
Timezone Intl.DateTimeFormat().resolvedOptions().timeZone String
Country Derived server-side from IP String (e.g. 'US')

Auto-detected events (when autoDetect is enabled)

Event Trigger
signup_start User visits a signup/register page
signup_submit User submits a form on a signup page
signup First identify() call (new user)
login_view User visits a login page
login_submit User submits a form on a login page
login Subsequent identify() call (returning user)
pricing_view User visits a pricing page
checkout_view User visits a checkout/cart page
checkout_submit User submits a form on a checkout page
success_view User visits a success/thank-you page
onboarding_view User visits an onboarding page
contact_view User visits a contact page
outbound_click User clicks an external link
Custom via data-ib-event User clicks an element with the attribute

NOT collected

  • IP addresses (never stored, used only for country lookup then discarded)
  • Browser fingerprint
  • Cookies
  • Personal data (name, email, etc.) unless you pass it to identify()
  • Cross-site tracking data

Privacy & Compliance

GDPR (EU)

The SDK does not use cookies and does not collect personal data by default. The anonymousId is a random identifier with no link to personal identity. Under GDPR, anonymous analytics may not require a consent banner — but consult your legal team.

When a user calls optOut(), all stored keys are cleared: _ib_anon, _ib_session, _ib_first_utm, _ib_last_utm, _ib_referrer, _ib_queue.

CCPA (California)

Global Privacy Control (GPC) is automatically honored. If a user has GPC enabled in their browser, the SDK sends no events. This is the legally correct behavior under CCPA as amended by CPRA.

Cookie Law (EU ePrivacy Directive)

No cookies are set. The localStorage identifiers are not cookies and fall outside the scope of the Cookie Law.


Self-Hosting

Point the SDK at your own IndieBob instance:

const bob = IndieBob.init({
  projectId: 'proj_abc123def456ghij',
  endpoint: 'https://your-indiebob.example.com/api/collect',
})

Versioning & Backwards Compatibility

This SDK follows Semantic Versioning.

1.x guarantee: All public methods listed in this README are stable. We will never:

  • Remove a method in a 1.x release
  • Change the behavior of an existing method without a deprecation period
  • Break the wire format for events that include v: 1

Upgrade path: npm update @indiebob/tracker — no code changes required within 1.x.

Deprecation process: Methods marked @deprecated in JSDoc will log a warning in debug: true mode. They remain functional until the next major version.


Bundle Size

Bundle Size (gzip)
Browser IIFE (tracker.js) < 5KB
ESM tree-shakeable < 4KB
Server (Node.js) No limit

Zero runtime dependencies.


License

MIT

Related Packages

@alexpodify/browser-sdk

BotCiu browser tracker — public, privacy-first interaction events (page/listing...

469
nuxt-measuremysite

Nuxt module for measuremy.site: cookieless web analytics your AI agent can read.

0 0
@nurdd/web-sdk

Framework-agnostic attribution & analytics SDK for the browser. Works with Vanil...

661
@outlit/browser

Browser tracking SDK for Outlit customer context with React and Vue bindings

73,549 6
@skoup/analytics-nuxt

Skoup Analytics for Nuxt 3 — a module: page views on navigation, useSkoup() ever...

630 1

Version History

Version Released Vue License
0.2.4 >=3.3.0 MIT
0.2.3 >=3.3.0 MIT
0.2.1 >=3.3.0 MIT
0.2.0 >=3.3.0 MIT
0.1.1 >=3.3.0 MIT