Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vemetric/vemetric/llms.txt

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

Overview

Following best practices when implementing Vemetric ensures clean, maintainable analytics that provide actionable insights. This guide covers recommended patterns for event naming, data structure, performance optimization, and data quality.
Well-structured analytics are easier to query, analyze, and share across your team. Invest time in planning your tracking strategy before implementation.

Event Naming Conventions

Use Descriptive, Action-Based Names

Event names should clearly describe what happened using past tense verbs:
vemetric.track('user_signed_up');
vemetric.track('purchase_completed');
vemetric.track('video_played');
vemetric.track('article_shared');
vemetric.track('filter_applied');

Naming Convention Rules

1

Use snake_case

All event names should use lowercase with underscores:
'checkout_started'
'payment_method_selected'
'CheckoutStarted'
'checkout-started'
'CHECKOUT_STARTED'
2

Start with object, end with action

Format: object_action or object_action_detail
'product_viewed'
'cart_item_added'
'subscription_plan_changed'
'viewed_product'
'add_to_cart'
3

Be specific but concise

Include enough detail to be clear, but avoid redundancy:
'trial_started'
'premium_trial_started' // If you have multiple trial types
'user_started_premium_trial_subscription' // Too verbose
'trial' // Not specific enough
4

Use consistent terminology

Standardize terms across your application:
// Choose one and stick with it
'post_created', 'post_edited', 'post_deleted'
'post_created', 'article_updated', 'content_removed'

// Document your terminology
// "post" = blog content
// "article" = help docs
// "content" = user-generated content

Event Naming Hierarchy

Organize events into logical categories:
// Authentication
'user_signed_up'
'user_logged_in'
'user_logged_out'
'password_reset_requested'
'password_reset_completed'

// E-commerce
'product_viewed'
'product_added_to_cart'
'cart_viewed'
'checkout_started'
'payment_info_entered'
'purchase_completed'

// Content Engagement
'article_viewed'
'article_liked'
'article_shared'
'comment_posted'
'video_played'
'video_completed'

// Feature Usage
'search_performed'
'filter_applied'
'export_downloaded'
'report_generated'

Event Properties

Structure Your Properties

Use clear, consistent property names with appropriate data types:
vemetric.track('purchase_completed', {
  // Identifiers
  order_id: 'ORD-12345',
  user_id: 'usr_abc123',
  
  // Metrics
  revenue: 99.99,
  quantity: 3,
  discount: 10.00,
  
  // Categories
  product_category: 'electronics',
  payment_method: 'credit_card',
  
  // Booleans
  is_first_purchase: true,
  gift_wrap_requested: false,
  
  // Metadata
  currency: 'USD',
  shipping_country: 'US'
});

Property Naming Guidelines

product_name, order_total, is_premium
productName, OrderTotal, isPremium
// Numbers for metrics
revenue: 99.99,          // ✅ Number
revenue: '$99.99',       // ❌ String

// Booleans for flags
is_premium: true,        // ✅ Boolean
is_premium: 'yes',       // ❌ String

// Strings for categories
category: 'electronics', // ✅ String
category: 1,             // ❌ Number

// Arrays for lists
tags: ['sale', 'new'],   // ✅ Array
tags: 'sale, new',       // ❌ String
// ✅ Flat structure (preferred)
{
  product_name: 'Widget',
  product_price: 29.99,
  product_category: 'tools'
}

// ❌ Nested structure (harder to query)
{
  product: {
    name: 'Widget',
    price: 29.99,
    category: 'tools'
  }
}
// ✅ Consistent across events
vemetric.track('product_viewed', {
  product_id: 'prod_123',
  product_name: 'Widget'
});

vemetric.track('product_purchased', {
  product_id: 'prod_123', // Same property name
  product_name: 'Widget'  // Same property name
});

// ❌ Inconsistent naming
vemetric.track('product_viewed', {
  product_id: 'prod_123',
  product_name: 'Widget'
});

vemetric.track('product_purchased', {
  id: 'prod_123',        // Different name!
  name: 'Widget'         // Different name!
});

Standard Property Names

Use these standardized property names for common attributes:
PropertyTypeDescriptionExample
*_idStringIdentifiersproduct_id, user_id, order_id
*_nameStringDisplay namesproduct_name, plan_name
*_categoryStringCategoriesproduct_category, content_category
*_countNumberCountsitem_count, view_count
*_amountNumberMonetary valuesorder_amount, refund_amount
is_*BooleanFlagsis_premium, is_trial, is_first_time
has_*BooleanPossessionhas_subscription, has_verified_email
*_atISO StringTimestampscreated_at, updated_at

User Properties

Setting User Data

User properties describe attributes of the user and should be updated when they change:
// On user signup
vemetric.identify('user@example.com', {
  displayName: 'Jane Doe',
  data: {
    set: {
      plan: 'premium',
      account_created_at: new Date().toISOString(),
      email_verified: false
    },
    setOnce: {
      signup_source: 'google_ads',
      initial_referrer: document.referrer
    }
  }
});

// Later, when email is verified
vemetric.updateUser({
  data: {
    set: {
      email_verified: true
    }
  }
});

set vs setOnce vs unset

Use for: Attributes that can change over time
data: {
  set: {
    plan: 'enterprise',      // Can upgrade/downgrade
    total_purchases: 42,     // Increases over time
    last_login: '2024-03-04', // Updates each login
    preferred_language: 'en' // User can change
  }
}
Behavior: Overwrites previous value each time

User Property Best Practices

Avoid: Storing data that changes with every event
// ❌ Bad: Storing event-specific data as user properties
vemetric.identify('user@example.com', {
  data: {
    set: {
      last_page_viewed: '/products/widget', // Changes constantly
      last_product_id: 'prod_123'          // Event-specific
    }
  }
});

// ✅ Good: Use event properties instead
vemetric.track('page_viewed', {
  page_path: '/products/widget'
});
Ensure consistent formatting:
// ✅ Normalized values
country: 'US',           // ISO codes
currency: 'USD',         // ISO codes
plan: 'premium',         // Lowercase
status: 'active',        // Lowercase

// ❌ Inconsistent values
country: 'United States', // Could be 'US', 'USA', 'United States'
currency: 'usd',          // Inconsistent casing
plan: 'Premium',          // Mixed casing
status: 'ACTIVE',         // All caps
// ✅ Clear and descriptive
{
  account_type: 'business',
  email_verified: true,
  trial_ends_at: '2024-04-01',
  feature_flags: ['advanced_analytics', 'api_access']
}

// ❌ Ambiguous or cryptic
{
  type: 'b',              // What type?
  flag1: true,            // Which flag?
  exp: '2024-04-01',      // Expiration? Experiment?
  ff: ['aa', 'api']       // Unclear abbreviations
}

Performance Optimization

Load SDK Asynchronously

Prevent blocking page render:
<!-- ✅ Async loading (recommended) -->
<script async src="https://cdn.vemetric.com/sdk.js"></script>
<script>
  window.addEventListener('load', function() {
    vemetric.init({ token: 'your-token' });
  });
</script>

<!-- ❌ Blocking load -->
<script src="https://cdn.vemetric.com/sdk.js"></script>
<script>
  vemetric.init({ token: 'your-token' });
</script>

Debounce High-Frequency Events

Avoid tracking every instance of rapid events:
// ❌ Bad: Tracks every scroll event (hundreds per page)
window.addEventListener('scroll', () => {
  vemetric.track('page_scrolled');
});

// ✅ Good: Debounced tracking
let scrollTimeout;
window.addEventListener('scroll', () => {
  clearTimeout(scrollTimeout);
  scrollTimeout = setTimeout(() => {
    const scrollPercentage = (window.scrollY / document.body.scrollHeight) * 100;
    vemetric.track('scroll_depth_reached', {
      depth_percentage: Math.round(scrollPercentage)
    });
  }, 500); // Wait 500ms after scrolling stops
});

// ✅ Better: Track milestone percentages
const milestones = [25, 50, 75, 100];
const reached = new Set();

window.addEventListener('scroll', () => {
  const scrollPercentage = (window.scrollY / document.body.scrollHeight) * 100;
  
  milestones.forEach(milestone => {
    if (scrollPercentage >= milestone && !reached.has(milestone)) {
      reached.add(milestone);
      vemetric.track('scroll_milestone_reached', {
        milestone_percentage: milestone
      });
    }
  });
});

Minimize Payload Size

// ❌ Bad: Sending large, unnecessary data
vemetric.track('form_submitted', {
  entire_form_html: document.querySelector('form').innerHTML, // Huge!
  all_form_data: getAllFormValues(), // Could contain sensitive data
  browser_history: window.history // Not needed
});

// ✅ Good: Send only relevant data
vemetric.track('form_submitted', {
  form_id: 'contact-form',
  field_count: 5,
  completion_time_seconds: 47,
  had_errors: false
});

Use Beacon API for Page Unload

Ensure events are sent even when user leaves page:
// ✅ Use sendBeacon for unload events
window.addEventListener('beforeunload', () => {
  // Beacon API ensures delivery even as page unloads
  navigator.sendBeacon(
    'https://hub.vemetric.com/e',
    JSON.stringify({
      name: 'session_ended',
      duration_seconds: sessionDuration
    })
  );
});

// Or use Vemetric's built-in method
vemetric.track('session_ended', {
  duration_seconds: sessionDuration
}, { beacon: true });

Data Quality

Validate Before Tracking

function trackPurchase(orderData) {
  // ❌ Bad: No validation
  vemetric.track('purchase_completed', orderData);
  
  // ✅ Good: Validate data first
  if (!orderData.order_id || !orderData.revenue) {
    console.error('Invalid order data', orderData);
    return;
  }
  
  vemetric.track('purchase_completed', {
    order_id: String(orderData.order_id),
    revenue: parseFloat(orderData.revenue),
    currency: orderData.currency || 'USD',
    item_count: parseInt(orderData.item_count) || 1
  });
}

Handle Missing Data Gracefully

// ✅ Use default values or omit properties
vemetric.track('product_viewed', {
  product_id: product.id,
  product_name: product.name,
  // Only include if available
  ...(product.category && { product_category: product.category }),
  ...(product.price && { product_price: product.price })
});

// Or use defaults
vemetric.track('product_viewed', {
  product_id: product.id,
  product_name: product.name || 'Unknown Product',
  product_category: product.category || 'uncategorized',
  product_price: product.price || 0
});

Add Error Tracking

// Track JavaScript errors
window.addEventListener('error', (event) => {
  vemetric.track('javascript_error', {
    error_message: event.message,
    error_source: event.filename,
    error_line: event.lineno,
    error_column: event.colno,
    page_url: window.location.href
  });
});

// Track unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  vemetric.track('unhandled_promise_rejection', {
    reason: event.reason?.message || String(event.reason),
    page_url: window.location.href
  });
});

Testing and QA

Development vs Production

Use different project tokens for different environments:
const VEMETRIC_TOKEN = process.env.NODE_ENV === 'production'
  ? 'prod_token_xyz'
  : 'dev_token_abc';

vemetric.init({
  token: VEMETRIC_TOKEN,
  debug: process.env.NODE_ENV !== 'production'
});

Test Checklist

1

Verify events in real-time

  • Open Vemetric dashboard
  • Go to Real-time view
  • Trigger events in your app
  • Confirm events appear with correct properties
2

Check user identification

  • Trigger identify() call
  • Verify user appears in Users section
  • Confirm user properties are set correctly
  • Test event attribution to identified user
3

Test across browsers

  • Chrome/Edge (Chromium)
  • Firefox
  • Safari
  • Mobile browsers (iOS Safari, Chrome Mobile)
4

Validate data types

// Add validation in development
if (process.env.NODE_ENV !== 'production') {
  const validateEvent = (name, properties) => {
    console.assert(typeof name === 'string', 'Event name must be string');
    console.assert(name === name.toLowerCase(), 'Event name should be lowercase');
    
    for (const [key, value] of Object.entries(properties)) {
      console.assert(
        key === key.toLowerCase(),
        `Property "${key}" should be lowercase`
      );
    }
  };
  
  // Wrap track method
  const originalTrack = vemetric.track;
  vemetric.track = (name, properties) => {
    validateEvent(name, properties);
    return originalTrack(name, properties);
  };
}

Documentation

Create a Tracking Plan

Document your events and properties:
# Tracking Plan

## Events

### user_signed_up
**Triggered when**: User completes signup form
**Properties**:
- `signup_method` (string): 'email', 'google', 'github'
- `referral_source` (string, optional): UTM source if present
- `is_email_verified` (boolean): Whether email was verified immediately

### purchase_completed
**Triggered when**: Order is successfully processed
**Properties**:
- `order_id` (string, required): Unique order identifier
- `revenue` (number, required): Total order value in dollars
- `currency` (string, required): ISO currency code (default: 'USD')
- `item_count` (number, required): Number of items in order
- `is_first_purchase` (boolean): Whether this is user's first order

Code Comments

Add context to tracking calls:
/**
 * Track checkout start
 * Fired when user clicks "Proceed to Checkout" button
 * Used for: Conversion funnel analysis, cart abandonment tracking
 */
vemetric.track('checkout_started', {
  cart_value: calculateCartTotal(),
  item_count: cart.items.length,
  has_discount: discountCode !== null
});

Common Patterns

Track Page Views

// Single Page Application (SPA)
router.afterEach((to, from) => {
  vemetric.track('page_view', {
    page_path: to.path,
    page_title: to.meta.title,
    previous_page: from.path
  });
});

// Traditional multi-page site
document.addEventListener('DOMContentLoaded', () => {
  vemetric.track('page_view', {
    page_path: window.location.pathname,
    page_title: document.title,
    referrer: document.referrer
  });
});

Track Form Interactions

// Form started
document.querySelectorAll('form').forEach(form => {
  let interacted = false;
  
  form.addEventListener('focusin', () => {
    if (!interacted) {
      interacted = true;
      vemetric.track('form_started', {
        form_id: form.id,
        form_name: form.getAttribute('name')
      });
    }
  });
  
  // Form submitted
  form.addEventListener('submit', () => {
    vemetric.track('form_submitted', {
      form_id: form.id,
      form_name: form.getAttribute('name')
    });
  });
});

Track Feature Usage

// When user uses a feature
function exportData() {
  vemetric.track('data_exported', {
    export_format: 'csv',
    row_count: data.length,
    feature: 'user_management'
  });
  
  // Perform export
  downloadCSV(data);
}

Next Steps

Privacy Compliance

Learn about GDPR/CCPA compliance and cookie management.

Troubleshooting

Debug common tracking issues and integration problems.