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.
Overview
All events you track with Grain are automatically sent to your analytics dashboard at grainql.com/dashboard . This guide shows you how to structure your events effectively to get the most actionable insights.
Custom Analytics UIs : Build custom analytics dashboards using the Query API . The Query API allows you to programmatically access your analytics data and create tailored experiences.
Accessing Your Analytics
After tracking events with the SDK, view them in your dashboard:
Go to grainql.com/dashboard
View real-time event streams
Analyze event properties and patterns
Filter by user IDs and properties
Build custom reports and visualizations
Export data for further analysis
Use Descriptive Event Names
Clear, specific event names make your analytics easier to understand:
// ✅ 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:
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:
// 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:
// 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:
// ✅ 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:
// 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:
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:
// 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:
// 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:
// 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:
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:
// 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:
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
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
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
grain . track ( 'feature_discovered' , { feature: 'export' });
grain . track ( 'feature_enabled' , { feature: 'export' });
grain . track ( 'feature_used' , { feature: 'export' , export_format: 'csv' });
Next Steps
Template Events Use pre-built event methods
User Properties Add user attributes for segmentation
E-commerce Guide Track commerce events effectively
Remote Config Dynamic configuration management