@grainql/tag
v4.4.6
Published
Grain Tag - Lightweight analytics SDK for Grain
Downloads
177
Maintainers
Readme
Installation
Script Tag (via Cloudflare Workers)
<script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>The SDK auto-initializes with your tenant config injected at the edge. No setup needed.
npm
npm install @grainql/tagQuick Start
Script Tag
<script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>
<script>
// Auto-initialized — start tracking immediately
GrainTag.track('button_clicked', { button: 'signup' });
// Identify a user after login
GrainTag.identify('user-123');
</script>npm (React, Next.js, Vue, Svelte, etc.)
import { init, track, identify } from '@grainql/tag';
// Initialize once
init({ tenantId: 'your-tenant-id' });
// Track events — even before init(), events are queued and replayed
track('page_viewed', { page: '/home' });
// Identify after login
identify('user-123');SSR / Server-Side
import { init, track } from '@grainql/tag';
// Safe to call on the server — returns a no-op stub, never crashes
const grain = init({ tenantId: 'your-tenant-id' });
track('page_viewed'); // silently ignored on server, works on clientConfiguration
init({
tenantId: 'your-tenant-id', // Required
apiUrl: 'https://clientapis.grainql.com', // Default
debug: false, // Enable debug logging
consentMode: 'auto', // 'auto' | 'opt-in' | 'opt-out'
enablePageViews: true, // Automatic page view tracking
enableHeatmaps: true, // Click and scroll heatmaps
enableSnapshots: true, // DOM snapshots for visual overlays
enableRescue: true, // Exit-intent rescue popups
batchSize: 50, // Events per batch
flushInterval: 5000, // Flush interval in ms
retryAttempts: 3, // Retry attempts on failure
});API Reference
Event Tracking
track(eventName, properties?) // Track a custom eventIdentity
identify(userId) // Set user identityConsent
grain.consent.grant(['analytics']) // Grant consent for categories
grain.consent.revoke() // Revoke consent
grain.consent.status() // Get current consent stateLifecycle
getInstance() // Get the current SDK instance
isInitialized() // Check if SDK is initialized
destroy() // Tear down and allow re-init
grain.flush() // Flush pending events
grain.isReady() // True if running in browserConsent Modes
| Mode | Default Behavior | On Consent |
|------|-----------------|------------|
| auto | Cookieless (daily rotating IDs) | Upgrades to permanent IDs |
| opt-in | Cookieless until explicit consent | Upgrades to permanent IDs |
| opt-out | Permanent IDs by default | Downgrades to cookieless on revoke |
How It Works
Events flow through a simple pipeline:
track()enqueues events into an in-memory batch — it never blocks.- Every 5 seconds (or when the batch reaches 50 events), events are flushed to the Grain API.
- Failed requests are retried with exponential backoff up to 3 times.
- On page unload or tab switch, remaining events are delivered via the Beacon API for reliable last-mile delivery.
- User identity uses daily rotating IDs by default (cookieless). On consent, it upgrades to persistent IDs stored in localStorage.
The SDK automatically attaches device, browser, os, UTM parameters, first-touch attribution, and session data to every event.
Building from Source
npm run build:sdk # Build SDK (IIFE, ESM, CJS + type declarations)
npm run build:worker # Build Cloudflare Worker
npm run build # Build everything
npm run build:dev # Development build (unminified)
npm run typecheck # TypeScript type checkingGrain SDKs
| Use Case | SDK |
|----------|-----|
| Browser analytics, heatmaps, snapshots | @grainql/tag (this package) |
| Script tag delivery (Cloudflare Workers) | @grainql/tag (this package) |
| Remote configuration | @grainql/analytics-web |
| Server-side event tracking (Node.js) | @grainql/analytics-web |
Migrating from @grainql/analytics-web
If you were using @grainql/analytics-web for browser analytics:
// Before
import { createGrainAnalytics } from '@grainql/analytics-web';
const grain = createGrainAnalytics({ tenantId: '...' });
grain.track('event');
// After
import { init, track } from '@grainql/tag';
init({ tenantId: '...' });
track('event');createGrainAnalytics()→init()grain.setUserId()→identify()- Consent API:
grain.grantConsent()→grain.consent.grant()
Contributing
See CONTRIBUTING.md.
