Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.grainql.com/llms.txt

Use this file to discover all available pages before exploring further.

What Are Events?

Events represent actions that happen in your application. Every time a user clicks a button, views a page, or completes a purchase, you can track it as an event. Think of events as a timeline of what users do in your app. By tracking these actions, you can:
  • Understand which features users engage with most
  • Identify where users drop off in your funnel
  • Measure the impact of changes to your product

Basic Event Tracking

The simplest way to track an event is with a name and properties:
// Using @grainql/tag (recommended for browser)
import { track } from '@grainql/tag';

track('button_clicked', {
  button_name: 'signup',
  page: '/home'
});
Anatomy of an event:
  • Event name (button_clicked): What happened
  • Properties (button_name, page): Context about the event
Event names should be clear and descriptive. Use past tense to indicate an action that already happened.
Want a systematic approach to naming? Our Event Naming Convention guide provides a hierarchical structure that makes events consistent and easy to query.

Event Properties

Properties add context to events. They answer questions like “which button?” or “how much?”:
import { track } from '@grainql/tag';

// Good: Rich context
track('product_viewed', {
  product_id: 'abc123',
  product_name: 'Blue Sneakers',
  category: 'footwear',
  price: 89.99
});

// Bad: Too vague
track('view');
Best practices:
  • Include relevant context (IDs, names, amounts)
  • Use consistent naming (snake_case or camelCase)
  • Avoid sensitive data (passwords, credit cards)
  • Keep property names simple and clear

Automatic Batching

Here’s where Grain saves you effort: events are automatically batched for efficiency. What is batching? Instead of sending each event immediately (slow and expensive), Grain collects events and sends them in groups.
import { track } from '@grainql/tag';

// These three events...
track('page_viewed', { page: '/products' });
track('button_clicked', { button: 'filter' });
track('search_performed', { query: 'shoes' });

// ...are batched and sent together
// after 5 seconds or when 50 events accumulate
Default behavior:
  • Events batch every 5 seconds
  • Or when 50 events accumulate
  • Whichever comes first
You can customize this:
import { init } from '@grainql/tag';

init({
  tenantId: 'your-tenant-id',
  batchSize: 100,        // Send after 100 events
  flushInterval: 10000   // Or every 10 seconds
});

Manual Flushing

Sometimes you need to send events immediately:
import { track, getInstance } from '@grainql/tag';

// Track a critical event
track('purchase_completed', {
  order_id: '12345',
  total: 99.99
});

// Force send immediately
const grain = getInstance();
await grain?.flush();
When to flush manually:
  • Critical events (purchases, signups)
  • Before app closes or logs out
  • In serverless functions
One-time flush: Pass flush: true to track():
import { track } from '@grainql/tag';

// This event sends immediately
await track('checkout_started', {
  cart_total: 149.99
}, { flush: true });

User Association

Events are more valuable when you know who performed them. Use identify() to associate events with a specific user:
import { identify, track } from '@grainql/tag';

// When user logs in
identify('user_123');

// All subsequent events include this user ID
track('feature_used', { feature: 'export' });
track('settings_changed', { setting: 'theme' });
Without a user ID, events are anonymous. This is fine for public websites, but for authenticated apps, always identify the user.

Event Lifecycle

Understanding what happens to events helps you track effectively:
  1. Track: You call track()
  2. Queue: Event added to internal queue
  3. Batch: Queue accumulates until batch size or interval
  4. Send: Batch sent to Grain API
  5. Retry: If failed, retry with exponential backoff
  6. Success: Event stored in Grain analytics
import { init } from '@grainql/tag';

// Configure retry behavior
init({
  tenantId: 'your-tenant-id',
  retryAttempts: 3  // Try 3 times if failed
});

Reliable Delivery

Grain Tag ensures events are not lost, even when:
  • Network is slow or fails temporarily
  • User closes the browser
  • App crashes
Automatic page exit handling: Grain Tag automatically listens for beforeunload, pagehide, and visibilitychange events and uses the Beacon API to flush pending events. You do not need to add your own event listeners for page exit — this is handled for you out of the box.

Common Patterns

Page View Tracking

Grain Tag auto-tracks page views (including SPA navigations via the History API), so you typically do not need to track these manually. If you need custom page view events:
import { track } from '@grainql/tag';

track('page_viewed', {
  page: window.location.pathname,
  title: document.title,
  referrer: document.referrer
});

Button Click Tracking

import { track } from '@grainql/tag';

button.addEventListener('click', () => {
  track('button_clicked', {
    button_name: button.id,
    button_text: button.textContent,
    page: window.location.pathname
  });
});

Form Submission

import { track } from '@grainql/tag';

form.addEventListener('submit', async (e) => {
  // Track before submission
  await track('form_submitted', {
    form_name: form.id,
    num_fields: form.elements.length
  }, { flush: true });

  // Continue with form submission
});

Debug Mode

Not sure if events are sending? Enable debug mode in two ways: Via URL parameter — add ?grain_debug=1 to any page URL. This enables debug logging in the browser console without changing code. Via config:
import { init } from '@grainql/tag';

init({
  tenantId: 'your-tenant-id',
  debug: true
});
You’ll see logs for queueing, batching, sending, and responses.

Next Steps

User Identification

Learn to track users across sessions

User Properties

Add attributes to user profiles

Template Events

Use pre-built event methods

API Reference

See all tracking methods
View your events in real-time at grainql.com/dashboard
These examples use @grainql/tag. If you’re using @grainql/analytics-web, replace imports with import { createGrainAnalytics } from '@grainql/analytics-web' and use grain.track() on the client instance.