> ## 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.

# Event Tracking Best Practices

> Structure your events for maximum insight in your analytics dashboard

## Overview

All events you track with Grain are automatically sent to your analytics dashboard at [grainql.com/dashboard](https://grainql.com/dashboard). This guide shows you how to structure your events effectively to get the most actionable insights.

<Note>
  **Custom Analytics UIs**: Build custom analytics dashboards using the [Query API](/guides/query-api). The Query API allows you to programmatically access your analytics data and create tailored experiences.
</Note>

## Accessing Your Analytics

After tracking events with the SDK, view them in your dashboard:

1. Go to [grainql.com/dashboard](https://grainql.com/dashboard)
2. View real-time event streams
3. Analyze event properties and patterns
4. Filter by user IDs and properties
5. Build custom reports and visualizations
6. Export data for further analysis

## Use Descriptive Event Names

Clear, specific event names make your analytics easier to understand:

```typescript theme={null}
// ✅ Good: Clear and specific
grain.track('checkout_completed', { orderId: '123', total: 99.99 });
grain.track('video_playback_started', { videoId: 'abc', duration: 120 });
grain.track('signup_form_submitted', { method: 'email' });

// ❌ Bad: Vague and generic
grain.track('action', { type: 'checkout' });
grain.track('event', { name: 'video' });
grain.track('click', { button: 'signup' });
```

Use underscores (`_`) for consistency and readability.

## Include Rich Context

Add relevant properties to every event for deeper analysis:

```typescript theme={null}
grain.track('product_viewed', {
  product_id: 'prod_123',
  product_name: 'Blue Sneakers',
  category: 'footwear',
  price: 89.99,
  currency: 'USD',
  source: 'search_results',
  referrer: document.referrer
});
```

The more context you provide, the better insights you'll get from your dashboard.

## Use Template Events

Grain provides pre-built template events for common scenarios:

```typescript theme={null}
// Login tracking with standard properties
await grain.trackLogin({
  method: 'email',
  success: true,
  twoFactorEnabled: false
});

// Purchase tracking with item details
await grain.trackPurchase({
  orderId: 'order_789',
  total: 149.99,
  currency: 'USD',
  items: [
    { id: 'item_1', name: 'Product A', price: 99.99, quantity: 1 },
    { id: 'item_2', name: 'Product B', price: 50.00, quantity: 1 }
  ],
  paymentMethod: 'credit_card'
});

// Add to cart tracking
await grain.trackAddToCart({
  itemId: 'prod_123',
  itemName: 'Blue Sneakers',
  price: 89.99,
  quantity: 1,
  category: 'footwear'
});
```

These template events ensure consistent tracking across your app.

## Track User Properties

Use properties to segment users in your dashboard:

```typescript theme={null}
// Set user attributes
await grain.setProperty({
  plan: 'premium',
  signup_date: '2024-01-15',
  total_purchases: '5'
});

// Properties let you filter and group users
grain.track('feature_used', { feature: 'export' });
```

You can set up to 4 properties per request. All values are converted to strings automatically.

## Consistent Naming Conventions

Use the same property names across related events:

```typescript theme={null}
// ✅ Good: Consistent property names
grain.track('video_started', {
  video_id: 'abc',
  video_title: 'Tutorial',
  duration_seconds: 120
});

grain.track('video_paused', {
  video_id: 'abc',
  video_title: 'Tutorial',
  duration_seconds: 120,
  current_time: 45
});

grain.track('video_completed', {
  video_id: 'abc',
  video_title: 'Tutorial',
  duration_seconds: 120
});

// ❌ Bad: Inconsistent naming
grain.track('video_started', { videoId: 'abc', length: 120 });
grain.track('video_paused', { video_id: 'abc', duration: 120 });
grain.track('video_completed', { id: 'abc', time: 120 });
```

Consistent naming makes it easier to analyze event sequences in your dashboard.

## Track User Journeys

Structure events to understand user flows:

```typescript theme={null}
// Track key steps in a conversion funnel
grain.track('signup_form_viewed');

grain.track('signup_form_started', {
  method: 'email'
});

grain.track('signup_form_submitted', {
  method: 'email'
});

await grain.trackSignup({
  method: 'email',
  source: 'landing_page',
  plan: 'free',
  success: true
});
```

Your dashboard can show where users drop off in multi-step processes.

## Session Tracking

Track session duration for engagement analysis:

```typescript theme={null}
function SessionTracker() {
  const track = useTrack();
  const sessionStart = useRef(Date.now());
  const sessionId = useRef(crypto.randomUUID());
  
  useEffect(() => {
    grain.track('session_started', {
      session_id: sessionId.current,
      page: window.location.pathname
    });
    
    return () => {
      const durationSeconds = Math.floor((Date.now() - sessionStart.current) / 1000);
      grain.track('session_ended', {
        session_id: sessionId.current,
        duration_seconds: durationSeconds
      }, { flush: true });
    };
  }, [track]);
  
  return null;
}
```

Use `flush: true` on session end to ensure the event is sent before the page unloads.

## Error and Exception Tracking

Track errors to identify and fix issues:

```typescript theme={null}
// JavaScript errors
window.addEventListener('error', (event) => {
  grain.track('javascript_error', {
    error_message: event.message,
    error_file: event.filename,
    error_line: event.lineno,
    page_url: window.location.pathname,
    user_agent: navigator.userAgent
  });
});

// Application errors
try {
  await processPayment();
} catch (error) {
  grain.track('payment_error', {
    error_type: error.name,
    error_message: error.message,
    payment_method: 'credit_card',
    amount: orderTotal
  });
  
  // Re-throw or handle
  throw error;
}
```

## Page View Tracking

Track page navigation for traffic analysis:

```typescript theme={null}
// Automatic page view tracking in React
function usePageTracking() {
  const track = useTrack();
  const location = useLocation();
  
  useEffect(() => {
    grain.trackPageView({
      page: location.pathname,
      title: document.title,
      referrer: document.referrer,
      url: window.location.href
    });
  }, [location, track]);
}

// In your app
function App() {
  usePageTracking();
  
  return <Routes>{/* your routes */}</Routes>;
}
```

## Use User Identification

Identify users to track them across sessions:

```typescript theme={null}
// After user logs in
grain.identify('user_123');

// Or use setUserId
grain.setUserId('user_123');

// Set user properties for segmentation
await grain.setProperty({
  email: 'user@example.com',
  plan: 'premium'
});

// All subsequent events will include the user ID
grain.track('dashboard_viewed');
```

Before login, Grain automatically generates and persists a unique anonymous ID for each user.

## Preload Remote Config

If you're using remote config, preload it for faster access:

```typescript theme={null}
import { GrainTag } from '@grainql/tag';

const grain = GrainTag.init({
  tenantId: 'your-tenant-id',
  defaultConfigurations: {
    feature_enabled: 'false',
    theme: 'light'
  }
});

// Preload configurations on app start
await grain.preloadConfig(
  ['feature_enabled', 'theme'], // Immediate keys
  { plan: 'premium' } // User properties for segmentation
);

// Access immediately (cache-first)
const featureEnabled = grain.getConfig('feature_enabled');
```

This ensures configs are available when you need them.

## Batch Events Automatically

Grain automatically batches events for efficiency:

```typescript theme={null}
// These events are queued and sent together
grain.track('button_clicked', { button: 'cta' });
grain.track('modal_opened', { modal: 'signup' });
grain.track('form_started', { form: 'signup' });

// Events are sent when:
// 1. Batch size reaches 50 events (configurable)
// 2. 5 seconds elapse (configurable)
// 3. Page unload occurs (automatic)
// 4. You call flush() manually
```

For critical events, force immediate send:

```typescript theme={null}
await grain.track('purchase_completed', {
  orderId: '123',
  total: 99.99
}, { flush: true });
```

## Best Practices Summary

**1. Descriptive Names**: Use clear, specific event names like `checkout_completed`

**2. Rich Properties**: Include relevant context in every event

**3. Consistent Naming**: Use the same property names across similar events

**4. Template Events**: Use built-in methods like `trackPurchase()` for consistency

**5. User Properties**: Set user attributes for segmentation

**6. Track Funnels**: Structure events to understand user journeys

**7. Error Tracking**: Log errors with detailed context

**8. User Identification**: Use `identify()` or `setUserId()` to track across sessions

**9. Flush Critical Events**: Use `flush: true` for important events like purchases

**10. Preload Config**: Load remote config early for faster access

## Common Patterns

### E-commerce Flow

```typescript theme={null}
grain.track('product_list_viewed', { category: 'shoes' });
grain.track('product_viewed', { product_id: 'prod_123' });
await grain.trackAddToCart({ itemId: 'prod_123', price: 89.99 });
await grain.trackCheckout({ total: 89.99, items: [...] });
await grain.trackPurchase({ orderId: 'order_789', total: 89.99 }, { flush: true });
```

### Authentication Flow

```typescript theme={null}
grain.track('login_form_viewed');
await grain.trackLogin({ method: 'email', success: true });
grain.identify('user_123');
await grain.setProperty({ plan: 'free', signup_date: '2024-01-15' });
```

### Feature Usage

```typescript theme={null}
grain.track('feature_discovered', { feature: 'export' });
grain.track('feature_enabled', { feature: 'export' });
grain.track('feature_used', { feature: 'export', export_format: 'csv' });
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Template Events" icon="sparkles" href="/core/template-events">
    Use pre-built event methods
  </Card>

  <Card title="User Properties" icon="tags" href="/core/user-properties">
    Add user attributes for segmentation
  </Card>

  <Card title="E-commerce Guide" icon="shopping-cart" href="/guides/ecommerce">
    Track commerce events effectively
  </Card>

  <Card title="Remote Config" icon="sliders" href="/core/remote-config">
    Dynamic configuration management
  </Card>
</CardGroup>
