Initial commit
This commit is contained in:
169
skills/ga4-custom-events/SKILL.md
Normal file
169
skills/ga4-custom-events/SKILL.md
Normal file
@@ -0,0 +1,169 @@
|
||||
---
|
||||
name: ga4-custom-events
|
||||
description: Expert guidance for creating business-specific custom events in GA4 beyond recommended events. Use when implementing custom tracking for unique business needs, creating industry-specific events (SaaS, education, media), defining custom event parameters, or tracking actions not covered by recommended events. Covers event naming conventions (snake_case, max 40 chars), parameter design, and implementation in gtag.js, GTM, and Measurement Protocol.
|
||||
---
|
||||
|
||||
# GA4 Custom Events
|
||||
|
||||
## Overview
|
||||
|
||||
Create business-specific custom events in Google Analytics 4 for tracking user interactions beyond Google's recommended events. Custom events enable measurement of unique business goals, industry-specific actions, and contextual behaviors that directly impact business outcomes. Master event naming conventions, parameter design strategies, and implementation across gtag.js, Google Tag Manager, and Measurement Protocol.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Invoke this skill when:
|
||||
- Creating custom event tracking for unique business actions
|
||||
- Designing event structure for SaaS, education, media, or specialized industries
|
||||
- Defining custom parameters and establishing naming conventions
|
||||
- Implementing custom events in gtag.js, GTM, or server-side
|
||||
- Registering custom parameters as custom dimensions
|
||||
- Building comprehensive event tracking strategy for business goals
|
||||
- Troubleshooting why custom events aren't appearing in GA4
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### Event Naming Conventions
|
||||
|
||||
Follow these principles for custom event names:
|
||||
|
||||
**Format & Constraints:**
|
||||
- Use snake_case (lowercase with underscores): `video_tutorial_watched`, `demo_requested`
|
||||
- Maximum 40 characters total length
|
||||
- Action-oriented: start with verb when possible
|
||||
- Descriptive and business-specific: avoid generic names like "event1", "click", "data"
|
||||
- Consistent across implementation: same event name everywhere
|
||||
|
||||
**Naming Framework:**
|
||||
```
|
||||
[Action]_[Object]_[Context]
|
||||
```
|
||||
|
||||
Examples:
|
||||
- `product_comparison_viewed` (user compared products)
|
||||
- `pricing_calculator_used` (engaged with pricing tool)
|
||||
- `whitepaper_downloaded` (downloaded resource)
|
||||
- `trial_signup_completed` (completed trial signup)
|
||||
- `support_ticket_created` (created support issue)
|
||||
|
||||
**Anti-patterns (avoid):**
|
||||
- Generic: `click`, `event`, `action`, `interaction`
|
||||
- Numbered: `event1`, `event2`, `custom_event_123`
|
||||
- Vague: `data_sent`, `tracking`, `user_action`
|
||||
- Inconsistent: mixing `video_watched`, `videoWatched`, `VideoWatched`
|
||||
|
||||
### Event Parameter Design
|
||||
|
||||
**Parameter Strategy:**
|
||||
|
||||
Identify what context makes the event meaningful:
|
||||
|
||||
1. **Identify the action:** What user behavior are you measuring?
|
||||
2. **Determine context:** What information would help analyze this action?
|
||||
3. **Define parameters:** Which data points provide that context?
|
||||
4. **Establish constraints:** Each parameter <100 characters
|
||||
|
||||
**Good Parameter Examples:**
|
||||
|
||||
For `course_enrollment`:
|
||||
```
|
||||
course_id: "COURSE_101"
|
||||
course_name: "Advanced GA4"
|
||||
instructor: "John Doe"
|
||||
price: 99.99
|
||||
currency: "USD"
|
||||
level: "advanced"
|
||||
```
|
||||
|
||||
For `support_ticket_created`:
|
||||
```
|
||||
ticket_type: "bug_report"
|
||||
product: "mobile_app"
|
||||
severity: "high"
|
||||
resolution_time_expected: 24
|
||||
department: "engineering"
|
||||
```
|
||||
|
||||
**Parameter Limits:**
|
||||
- Maximum 25 parameters per event
|
||||
- Parameter names: 40 characters maximum
|
||||
- Parameter values: 100 characters maximum (exceptions: page_location 1000)
|
||||
- Use string, integer, or float types
|
||||
|
||||
### Industry-Specific Patterns
|
||||
|
||||
**SaaS Events:**
|
||||
- `trial_started`, `trial_ended`, `upgrade_initiated`, `plan_downgraded`
|
||||
- Include: plan_type, feature_count, estimated_value
|
||||
|
||||
**Education Events:**
|
||||
- `lesson_completed`, `quiz_submitted`, `certificate_earned`
|
||||
- Include: subject, difficulty_level, score_percentage
|
||||
|
||||
**E-commerce Events:**
|
||||
- Beyond `purchase`: `product_compared`, `review_submitted`, `wishlist_added`
|
||||
- Include: product_category, competitor_product, star_rating
|
||||
|
||||
**Media Events:**
|
||||
- `article_shared`, `video_watched`, `podcast_episode_completed`
|
||||
- Include: content_type, duration, engagement_percentage
|
||||
|
||||
### Implementation Across Platforms
|
||||
|
||||
**gtag.js Implementation:**
|
||||
```javascript
|
||||
gtag('event', 'demo_requested', {
|
||||
'demo_type': 'product_walkthrough',
|
||||
'industry': 'technology',
|
||||
'company_size': 'enterprise',
|
||||
'email_domain': 'company.com'
|
||||
});
|
||||
```
|
||||
|
||||
**GTM Data Layer Implementation:**
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'demo_requested',
|
||||
'demo_type': 'product_walkthrough',
|
||||
'industry': 'technology'
|
||||
});
|
||||
```
|
||||
|
||||
**Measurement Protocol (Server-Side):**
|
||||
```python
|
||||
event_data = {
|
||||
"client_id": "123.456",
|
||||
"events": [{
|
||||
"name": "demo_requested",
|
||||
"params": {
|
||||
"demo_type": "product_walkthrough",
|
||||
"industry": "technology"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Registration as Custom Dimensions
|
||||
|
||||
Custom parameters won't appear in GA4 reports until registered:
|
||||
|
||||
1. Send parameter in event (any platform)
|
||||
2. Admin → Data Display → Custom Definitions → Create Custom Dimension
|
||||
3. Configure: Dimension Name, Scope (Event/User/Item), Event Parameter (exact name)
|
||||
4. Save and wait 24-48 hours for data to populate
|
||||
|
||||
## References
|
||||
|
||||
- **references/naming-conventions-guide.md** - Complete naming conventions, patterns, and examples
|
||||
- **references/event-design-framework.md** - Event architecture, parameter scoping, and design workflows
|
||||
- **references/industry-patterns.md** - Industry-specific events for SaaS, education, media, ecommerce
|
||||
- **references/parameter-strategy.md** - Parameter design, validation, and best practices
|
||||
|
||||
## Integration with Other Skills
|
||||
|
||||
- **ga4-events-fundamentals** - Understanding GA4 event architecture and scopes (prerequisite)
|
||||
- **ga4-recommended-events** - Recommended events that complement custom events
|
||||
- **ga4-gtag-implementation** - Implementing custom events via gtag.js
|
||||
- **ga4-gtm-integration** - Implementing custom events via Google Tag Manager
|
||||
- **ga4-measurement-protocol** - Server-side custom event implementation
|
||||
- **ga4-custom-dimensions** - Registering custom parameters as reportable dimensions
|
||||
- **ga4-debugview** - Testing and validating custom events before production
|
||||
600
skills/ga4-custom-events/assets/custom-event-templates.js
Normal file
600
skills/ga4-custom-events/assets/custom-event-templates.js
Normal file
@@ -0,0 +1,600 @@
|
||||
/**
|
||||
* GA4 Custom Event Templates
|
||||
* Ready-to-use implementations for common business scenarios
|
||||
* Modify event names and parameters to match your business logic
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// SAAS EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* SaaS Free Trial Signup
|
||||
* Track when user initiates a free trial
|
||||
*/
|
||||
function trackTrialSignup(planType, source) {
|
||||
gtag('event', 'trial_signup', {
|
||||
'trial_plan': planType, // 'professional', 'enterprise'
|
||||
'trial_days': 30,
|
||||
'source': source, // 'landing_page', 'email', 'ad'
|
||||
'campaign': getUTMParameter('utm_campaign')
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SaaS Trial to Paid Conversion
|
||||
* Track when trial user converts to paid subscription
|
||||
*/
|
||||
function trackTrialConversion(trialPlan, paidPlan, conversionValue) {
|
||||
gtag('event', 'trial_conversion', {
|
||||
'trial_plan': trialPlan,
|
||||
'paid_plan': paidPlan,
|
||||
'conversion_value': conversionValue,
|
||||
'trial_duration_days': calculateTrialDays(),
|
||||
'most_used_feature': getMostUsedFeature()
|
||||
});
|
||||
|
||||
// Also set as user property
|
||||
gtag('set', {
|
||||
'subscription_plan': paidPlan,
|
||||
'subscription_status': 'active'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SaaS Feature Adoption
|
||||
* Track when user enables/activates a premium feature
|
||||
*/
|
||||
function trackFeatureAdoption(featureName, featureCategory) {
|
||||
gtag('event', 'feature_enable', {
|
||||
'feature_name': featureName, // 'team_collaboration', 'api_access'
|
||||
'feature_category': featureCategory, // 'premium', 'beta', 'advanced'
|
||||
'user_plan': getCurrentUserPlan(),
|
||||
'days_since_signup': calculateDaysSinceSignup(),
|
||||
'discovery_method': 'settings' // 'settings', 'email', 'notification'
|
||||
});
|
||||
|
||||
// Track as user property if persistent feature
|
||||
gtag('set', {
|
||||
'features_enabled': incrementFeatureCount()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SaaS Subscription Upgrade
|
||||
* Track plan upgrades
|
||||
*/
|
||||
function trackSubscriptionUpgrade(fromPlan, toPlan) {
|
||||
const priceIncrease = calculatePriceIncrease(fromPlan, toPlan);
|
||||
|
||||
gtag('event', 'subscription_upgrade', {
|
||||
'from_plan': fromPlan,
|
||||
'to_plan': toPlan,
|
||||
'upgrade_value': priceIncrease,
|
||||
'currency': 'USD',
|
||||
'customer_tenure_months': getCustomerTenure()
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'subscription_plan': toPlan,
|
||||
'monthly_recurring_revenue': getPlanPrice(toPlan)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SaaS Subscription Cancellation
|
||||
* Track when user cancels subscription
|
||||
*/
|
||||
function trackSubscriptionCancel(plan, reason) {
|
||||
gtag('event', 'subscription_cancel', {
|
||||
'plan': plan,
|
||||
'reason': reason, // 'too_expensive', 'not_using', 'switching'
|
||||
'tenure_months': getCustomerTenure(),
|
||||
'mrr': getMRR(),
|
||||
'ltv': getCustomerLTV()
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'subscription_status': 'cancelled'
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// E-COMMERCE EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Product View
|
||||
* Track when user views product page
|
||||
*/
|
||||
function trackProductView(productId, productName, price, category) {
|
||||
gtag('event', 'view_item', {
|
||||
'items': [
|
||||
{
|
||||
'item_id': productId,
|
||||
'item_name': productName,
|
||||
'price': price,
|
||||
'item_category': category,
|
||||
'item_variant': getProductVariant(),
|
||||
'item_brand': getProductBrand()
|
||||
}
|
||||
],
|
||||
'value': price,
|
||||
'currency': 'USD',
|
||||
'source_page': getCurrentPage()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to Cart
|
||||
* Track when user adds product to shopping cart
|
||||
*/
|
||||
function trackAddToCart(productId, productName, quantity, price) {
|
||||
gtag('event', 'add_to_cart', {
|
||||
'items': [
|
||||
{
|
||||
'item_id': productId,
|
||||
'item_name': productName,
|
||||
'quantity': quantity,
|
||||
'price': price,
|
||||
'item_category': getProductCategory(productId)
|
||||
}
|
||||
],
|
||||
'value': price * quantity,
|
||||
'currency': 'USD'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin Checkout
|
||||
* Track when user starts checkout process
|
||||
*/
|
||||
function trackBeginCheckout(items, cartValue) {
|
||||
gtag('event', 'begin_checkout', {
|
||||
'items': items.map(item => ({
|
||||
'item_id': item.id,
|
||||
'item_name': item.name,
|
||||
'quantity': item.quantity,
|
||||
'price': item.price
|
||||
})),
|
||||
'value': cartValue,
|
||||
'currency': 'USD',
|
||||
'coupon_applied': hasCoupon()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase Event
|
||||
* Track completed purchase
|
||||
*/
|
||||
function trackPurchase(transactionId, items, total, tax, shipping, coupon) {
|
||||
gtag('event', 'purchase', {
|
||||
'transaction_id': transactionId,
|
||||
'affiliation': getStoreName(),
|
||||
'value': total,
|
||||
'currency': 'USD',
|
||||
'tax': tax,
|
||||
'shipping': shipping,
|
||||
'coupon': coupon,
|
||||
'items': items.map(item => ({
|
||||
'item_id': item.id,
|
||||
'item_name': item.name,
|
||||
'item_category': item.category,
|
||||
'item_brand': item.brand,
|
||||
'price': item.price,
|
||||
'quantity': item.quantity
|
||||
}))
|
||||
});
|
||||
|
||||
// Update user properties
|
||||
gtag('set', {
|
||||
'purchase_count': incrementPurchaseCount(),
|
||||
'customer_lifetime_value': updateLTV(),
|
||||
'last_purchase_date': getCurrentDate()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Product Review Submission
|
||||
* Track when user submits product review
|
||||
*/
|
||||
function trackProductReview(productId, rating, reviewLength) {
|
||||
gtag('event', 'product_review_submit', {
|
||||
'product_id': productId,
|
||||
'rating': rating, // 1-5 stars
|
||||
'review_length': reviewLength, // character count
|
||||
'verified_purchase': isVerifiedPurchase()
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EDUCATION/LEARNING EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Course Enrollment
|
||||
* Track when user enrolls in a course
|
||||
*/
|
||||
function trackCourseEnroll(courseId, courseName, price) {
|
||||
gtag('event', 'course_enroll', {
|
||||
'course_id': courseId,
|
||||
'course_name': courseName,
|
||||
'course_level': getCourseLevel(courseId),
|
||||
'price': price,
|
||||
'currency': 'USD',
|
||||
'enrollment_method': 'direct' // 'direct', 'subscription', 'free'
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'courses_enrolled': incrementEnrolledCount(),
|
||||
'total_spent_on_courses': updateCoursesSpent(price)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lesson Completion
|
||||
* Track when user finishes a lesson
|
||||
*/
|
||||
function trackLessonComplete(courseId, lessonId, timeSpentMinutes, score) {
|
||||
gtag('event', 'lesson_complete', {
|
||||
'course_id': courseId,
|
||||
'lesson_id': lessonId,
|
||||
'lesson_title': getLessonTitle(lessonId),
|
||||
'time_spent_minutes': timeSpentMinutes,
|
||||
'completion_score': score, // percentage or points
|
||||
'is_required': isRequiredLesson(lessonId)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Submission
|
||||
* Track when user submits quiz
|
||||
*/
|
||||
function trackQuizSubmit(courseId, quizId, correctAnswers, totalQuestions, score) {
|
||||
gtag('event', 'quiz_submit', {
|
||||
'course_id': courseId,
|
||||
'quiz_id': quizId,
|
||||
'questions_correct': correctAnswers,
|
||||
'questions_total': totalQuestions,
|
||||
'score_percent': Math.round((correctAnswers / totalQuestions) * 100),
|
||||
'attempts': getQuizAttemptCount(),
|
||||
'passed': score >= getPassingScore()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Certificate Earned
|
||||
* Track when user earns course certificate
|
||||
*/
|
||||
function trackCertificateEarn(courseId, certificateType, finalScore) {
|
||||
gtag('event', 'certificate_earn', {
|
||||
'course_id': courseId,
|
||||
'certificate_type': certificateType, // 'completion', 'achievement'
|
||||
'final_score': finalScore,
|
||||
'days_to_completion': calculateDaysToCompletion(),
|
||||
'shared_socially': false // will update if shared later
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'certificates_earned': incrementCertificateCount(),
|
||||
'total_learning_hours': updateTotalHours()
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CONTENT/MEDIA EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Article/Blog Post View
|
||||
* Track when user reads article
|
||||
*/
|
||||
function trackArticleView(articleId, articleTitle, category, authorId) {
|
||||
gtag('event', 'article_view', {
|
||||
'article_id': articleId,
|
||||
'article_title': articleTitle,
|
||||
'article_category': category,
|
||||
'author_id': authorId,
|
||||
'article_word_count': getArticleWordCount(),
|
||||
'publication_date': getArticleDate()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Article Read Complete
|
||||
* Track when user reads entire article
|
||||
*/
|
||||
function trackArticleReadComplete(articleId, timeOnPageSeconds) {
|
||||
gtag('event', 'article_read_complete', {
|
||||
'article_id': articleId,
|
||||
'time_on_page_seconds': timeOnPageSeconds,
|
||||
'estimated_reading_time_seconds': getEstimatedReadingTime(articleId),
|
||||
'reading_efficiency': calculateReadingEfficiency(timeOnPageSeconds)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Video Watch Start
|
||||
* Track when user starts watching video
|
||||
*/
|
||||
function trackVideoStart(videoId, videoTitle, duration, category) {
|
||||
gtag('event', 'video_start', {
|
||||
'video_id': videoId,
|
||||
'video_title': videoTitle,
|
||||
'video_duration_seconds': duration,
|
||||
'video_category': category
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Video Complete
|
||||
* Track when user completes video
|
||||
*/
|
||||
function trackVideoComplete(videoId, watchTimeSeconds, duration) {
|
||||
const completionPercent = Math.round((watchTimeSeconds / duration) * 100);
|
||||
|
||||
gtag('event', 'video_complete', {
|
||||
'video_id': videoId,
|
||||
'watch_time_seconds': watchTimeSeconds,
|
||||
'video_duration_seconds': duration,
|
||||
'completion_percent': completionPercent,
|
||||
'quality_watched': getPlaybackQuality(),
|
||||
'watch_count': incrementVideoWatchCount()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Content Share
|
||||
* Track when user shares content
|
||||
*/
|
||||
function trackContentShare(contentId, contentType, method) {
|
||||
gtag('event', 'content_share', {
|
||||
'content_id': contentId,
|
||||
'content_type': contentType, // 'article', 'video', 'resource'
|
||||
'share_method': method, // 'email', 'twitter', 'facebook', 'linkedin'
|
||||
'timestamp': getCurrentTime()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Newsletter Signup
|
||||
* Track when user subscribes to newsletter
|
||||
*/
|
||||
function trackNewsletterSignup(category, frequency) {
|
||||
gtag('event', 'newsletter_subscribe', {
|
||||
'newsletter_category': category, // 'weekly_digest', 'industry_news'
|
||||
'frequency': frequency, // 'daily', 'weekly', 'monthly'
|
||||
'email_validated': true,
|
||||
'signup_source': getSignupSource() // 'exit_intent', 'footer', 'sidebar'
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'newsletter_subscribed': true,
|
||||
'newsletter_categories': category
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SUPPORT/HELP EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Support Ticket Created
|
||||
* Track when user opens support ticket
|
||||
*/
|
||||
function trackSupportTicket(category, severity, userPlan) {
|
||||
gtag('event', 'support_ticket_create', {
|
||||
'ticket_category': category, // 'billing', 'technical', 'feature_request'
|
||||
'severity': severity, // 'low', 'medium', 'high', 'critical'
|
||||
'user_plan': userPlan,
|
||||
'response_time_expectation': 'within_24_hours'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Help Article View
|
||||
* Track when user searches for help
|
||||
*/
|
||||
function trackHelpSearch(searchTerm, resultsCount, articleClicked) {
|
||||
gtag('event', 'help_search', {
|
||||
'search_term': searchTerm,
|
||||
'search_results_count': resultsCount,
|
||||
'article_clicked': articleClicked,
|
||||
'search_category': 'knowledge_base' // 'knowledge_base', 'faq', 'documentation'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature Request Submitted
|
||||
* Track when user requests new feature
|
||||
*/
|
||||
function trackFeatureRequest(featureName, useCase) {
|
||||
gtag('event', 'feature_request_submit', {
|
||||
'requested_feature': featureName,
|
||||
'use_case': useCase,
|
||||
'upvote_count': 1
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SIGNUP/AUTHENTICATION EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* User Signup
|
||||
* Track account creation
|
||||
*/
|
||||
function trackUserSignup(signupMethod, source) {
|
||||
gtag('event', 'user_signup', {
|
||||
'signup_method': signupMethod, // 'email', 'google', 'facebook', 'github'
|
||||
'signup_source': source, // 'landing_page', 'app', 'email'
|
||||
'email_verified': false // Will update when verified
|
||||
});
|
||||
|
||||
// Set user properties
|
||||
gtag('set', {
|
||||
'signup_date': getCurrentDate(),
|
||||
'signup_method': signupMethod,
|
||||
'email_verified': false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Email Verification
|
||||
* Track when user verifies email
|
||||
*/
|
||||
function trackEmailVerified() {
|
||||
gtag('event', 'email_verified', {
|
||||
'verification_method': 'email_link', // 'email_link', 'sms_code', 'oauth'
|
||||
'time_to_verify_minutes': calculateMinutesSinceSignup()
|
||||
});
|
||||
|
||||
// Update user property
|
||||
gtag('set', {
|
||||
'email_verified': true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* User Login
|
||||
* Track when user logs in
|
||||
*/
|
||||
function trackUserLogin(loginMethod, isFirstLogin) {
|
||||
gtag('event', 'user_login', {
|
||||
'login_method': loginMethod, // 'password', 'sso', 'social'
|
||||
'is_first_login': isFirstLogin,
|
||||
'platform': getPlatform() // 'web', 'mobile', 'desktop'
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ACCOUNT MANAGEMENT EVENT TEMPLATES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Profile Completion
|
||||
* Track user profile progress
|
||||
*/
|
||||
function trackProfileComplete(completionPercent, fieldsUpdated) {
|
||||
gtag('event', 'profile_complete', {
|
||||
'completion_percent': completionPercent,
|
||||
'fields_updated': fieldsUpdated,
|
||||
'has_profile_photo': hasProfilePhoto(),
|
||||
'has_bio': hasBio()
|
||||
});
|
||||
|
||||
if (completionPercent === 100) {
|
||||
gtag('set', {
|
||||
'profile_complete': true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings Changed
|
||||
* Track when user changes account settings
|
||||
*/
|
||||
function trackSettingsChange(settingType, newValue) {
|
||||
gtag('event', 'settings_change', {
|
||||
'setting_type': settingType, // 'language', 'timezone', 'notifications'
|
||||
'new_value': newValue,
|
||||
'previous_value': getPreviousValue(settingType)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment Method Added
|
||||
* Track when user adds payment method
|
||||
*/
|
||||
function trackPaymentMethodAdd(paymentType) {
|
||||
gtag('event', 'payment_method_add', {
|
||||
'payment_type': paymentType, // 'credit_card', 'paypal', 'bank_transfer'
|
||||
'payment_methods_count': incrementPaymentMethodCount(),
|
||||
'is_default': false
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HELPER FUNCTIONS (Replace with your actual implementation)
|
||||
// ============================================================================
|
||||
|
||||
function getUTMParameter(param) {
|
||||
const url = new URLSearchParams(window.location.search);
|
||||
return url.get(param) || '';
|
||||
}
|
||||
|
||||
function getCurrentUserPlan() {
|
||||
// Get from your user data/API
|
||||
return localStorage.getItem('userPlan') || 'free';
|
||||
}
|
||||
|
||||
function calculateTrialDays() {
|
||||
// Calculate days from trial start to now
|
||||
const trialStart = new Date(localStorage.getItem('trialStartDate'));
|
||||
return Math.floor((new Date() - trialStart) / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
|
||||
function getMostUsedFeature() {
|
||||
// Get from your analytics/tracking
|
||||
return 'reporting';
|
||||
}
|
||||
|
||||
function getCustomerTenure() {
|
||||
// Get from your database
|
||||
return parseInt(localStorage.getItem('customerTenureMonths')) || 0;
|
||||
}
|
||||
|
||||
function calculateDaysSinceSignup() {
|
||||
const signupDate = new Date(localStorage.getItem('signupDate'));
|
||||
return Math.floor((new Date() - signupDate) / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
|
||||
function getCurrentPage() {
|
||||
return window.location.pathname;
|
||||
}
|
||||
|
||||
function getProductCategory(productId) {
|
||||
// Get from your product database
|
||||
return 'electronics';
|
||||
}
|
||||
|
||||
function getCurrentDate() {
|
||||
return new Date().toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
function getCurrentTime() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function getPlatform() {
|
||||
return /Mobile|Android|iPhone/.test(navigator.userAgent) ? 'mobile' : 'web';
|
||||
}
|
||||
|
||||
// Export for use in other files
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
trackTrialSignup,
|
||||
trackTrialConversion,
|
||||
trackFeatureAdoption,
|
||||
trackSubscriptionUpgrade,
|
||||
trackSubscriptionCancel,
|
||||
trackProductView,
|
||||
trackAddToCart,
|
||||
trackBeginCheckout,
|
||||
trackPurchase,
|
||||
trackCourseEnroll,
|
||||
trackLessonComplete,
|
||||
trackVideoStart,
|
||||
trackVideoComplete,
|
||||
trackNewsletterSignup,
|
||||
trackUserSignup,
|
||||
trackUserLogin,
|
||||
trackProfileComplete,
|
||||
trackSettingsChange
|
||||
};
|
||||
}
|
||||
401
skills/ga4-custom-events/references/event-design-framework.md
Normal file
401
skills/ga4-custom-events/references/event-design-framework.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# GA4 Event Design Framework
|
||||
|
||||
## Comprehensive Guide to Custom Event Architecture
|
||||
|
||||
### Event Design Philosophy
|
||||
|
||||
Custom events should answer business questions:
|
||||
- **What action** is the user taking?
|
||||
- **Why does it matter** to the business?
|
||||
- **What context** makes it meaningful?
|
||||
- **How will it be analyzed**?
|
||||
|
||||
Effective events provide actionable insights about user behavior, feature adoption, and business metrics.
|
||||
|
||||
---
|
||||
|
||||
## Event Discovery Process
|
||||
|
||||
### Step 1: Identify Business Goals
|
||||
|
||||
List objectives that matter to the business:
|
||||
- User acquisition and activation
|
||||
- Feature adoption and engagement
|
||||
- Conversion and monetization
|
||||
- Retention and churn prevention
|
||||
- Support and satisfaction
|
||||
|
||||
### Step 2: Map User Actions to Goals
|
||||
|
||||
Connect specific user behaviors to each business goal:
|
||||
|
||||
| Business Goal | User Action | Event Needed |
|
||||
|---------------|------------|--------------|
|
||||
| Feature adoption | User explores premium feature | `feature_explore` |
|
||||
| Conversion | User upgrades from trial | `upgrade_start` |
|
||||
| Retention | User invites teammates | `user_invite_send` |
|
||||
| Support | User opens help article | `help_article_view` |
|
||||
| Churn prevention | User pauses subscription | `subscription_pause` |
|
||||
|
||||
### Step 3: Determine Required Context
|
||||
|
||||
For each action, identify what context makes analysis meaningful:
|
||||
|
||||
**Example: Feature Adoption**
|
||||
```
|
||||
Event: feature_explore
|
||||
Context needed:
|
||||
- feature_name: Which feature?
|
||||
- feature_category: Premium, beta, experimental?
|
||||
- source: Where did they find it? (help, notification, suggestion)
|
||||
- result: Did they stay on feature?
|
||||
```
|
||||
|
||||
### Step 4: Design Event Structure
|
||||
|
||||
Finalize event name and parameters:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'feature_explore', {
|
||||
'feature_name': 'advanced_reporting',
|
||||
'feature_category': 'premium',
|
||||
'discovery_source': 'help_article',
|
||||
'user_tier': 'free'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Scoping Strategy
|
||||
|
||||
GA4 uses three scopes. Choose correctly:
|
||||
|
||||
### Event-Scoped Parameters (Single Event)
|
||||
|
||||
**Use When:** Data is specific to this event occurrence
|
||||
|
||||
Examples:
|
||||
- `button_clicked` event: which button_name?
|
||||
- `video_watched` event: which video_title?
|
||||
- `form_submit` event: which form_name, form_destination?
|
||||
|
||||
```javascript
|
||||
gtag('event', 'video_watched', {
|
||||
'video_title': 'GA4 Fundamentals', // Event-scoped
|
||||
'video_duration': 600, // Event-scoped
|
||||
'completion_percent': 100 // Event-scoped
|
||||
});
|
||||
```
|
||||
|
||||
**Registration:** Event-Scoped Custom Dimension in Admin
|
||||
|
||||
### User-Scoped Parameters (User Properties)
|
||||
|
||||
**Use When:** Data applies to ALL events from user
|
||||
|
||||
Examples:
|
||||
- subscription tier (free, pro, enterprise)
|
||||
- company size (startup, mid-market, enterprise)
|
||||
- customer segment (high-value, at-risk, churned)
|
||||
- geographic region (EMEA, APAC, Americas)
|
||||
|
||||
```javascript
|
||||
gtag('set', {
|
||||
'subscription_tier': 'premium', // User-scoped
|
||||
'customer_segment': 'high_value', // User-scoped
|
||||
'onboarding_complete': true // User-scoped
|
||||
});
|
||||
```
|
||||
|
||||
**When to Use:** Set once, applies to all subsequent events
|
||||
|
||||
**Registration:** User-Scoped Custom Dimension in Admin
|
||||
|
||||
### Item-Scoped Parameters (Ecommerce Items)
|
||||
|
||||
**Use When:** Data applies to individual products in transaction
|
||||
|
||||
Only used with items array in ecommerce events.
|
||||
|
||||
```javascript
|
||||
gtag('event', 'purchase', {
|
||||
'items': [
|
||||
{
|
||||
'item_id': 'SKU_001',
|
||||
'item_name': 'Premium Widget',
|
||||
'item_color': 'blue', // Item-scoped
|
||||
'supplier': 'Vendor A', // Item-scoped
|
||||
'quantity': 2,
|
||||
'price': 49.99
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Design Best Practices
|
||||
|
||||
### Rule 1: Keep Parameters Meaningful
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
gtag('event', 'trial_start', {
|
||||
'trial_days': 30,
|
||||
'trial_plan': 'professional',
|
||||
'source_utm': 'google_ads'
|
||||
});
|
||||
|
||||
❌ Bad:
|
||||
gtag('event', 'trial_start', {
|
||||
'random_id': '12345',
|
||||
'session_date': '2024-11-10',
|
||||
'browser': 'Chrome' // These are auto-collected
|
||||
});
|
||||
```
|
||||
|
||||
### Rule 2: Use Consistent Values
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
Always use 'free', 'pro', 'enterprise'
|
||||
(case consistent, lowercase)
|
||||
|
||||
❌ Bad:
|
||||
Mix of 'free', 'Free', 'FREE', 'standard'
|
||||
(inconsistent capitalization)
|
||||
```
|
||||
|
||||
### Rule 3: Avoid High-Cardinality Parameters
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
'plan_type': 'pro' (5 possible values)
|
||||
|
||||
❌ Bad:
|
||||
'user_email': 'user@example.com' (millions of values)
|
||||
'session_id': '12345abc...' (unique per session)
|
||||
```
|
||||
|
||||
### Rule 4: Store IDs as Parameters, Not in Event Name
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
gtag('event', 'video_watched', {
|
||||
'video_id': 'VID_12345',
|
||||
'video_title': 'Learning GA4'
|
||||
});
|
||||
|
||||
❌ Bad:
|
||||
gtag('event', 'video_watched_VID_12345')
|
||||
gtag('event', 'video_watched_learning_ga4')
|
||||
```
|
||||
|
||||
### Rule 5: Use Numeric Types for Metrics
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
'price': 99.99 (number)
|
||||
'quantity': 5 (integer)
|
||||
'duration_seconds': 1200 (integer)
|
||||
|
||||
❌ Bad:
|
||||
'price': '$99.99' (string with currency)
|
||||
'quantity': 'five' (string)
|
||||
'duration': '20 minutes' (string)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event Documentation Template
|
||||
|
||||
Document every custom event:
|
||||
|
||||
```markdown
|
||||
### Event: demo_request
|
||||
|
||||
**Description:** User requests product demonstration
|
||||
|
||||
**When Fired:**
|
||||
- User clicks "Request Demo" button on pricing page
|
||||
- User submits demo request form
|
||||
|
||||
**Parameters:**
|
||||
| Parameter | Type | Values | Purpose |
|
||||
|-----------|------|--------|---------|
|
||||
| demo_type | string | walkthrough, personalized | Type of demo requested |
|
||||
| industry | string | tech, finance, healthcare | User's industry |
|
||||
| company_size | string | startup, mid-market, enterprise | Company size |
|
||||
| source | string | pricing_page, landing_page, email | Where request originated |
|
||||
|
||||
**Implementation:**
|
||||
- gtag.js: Yes
|
||||
- GTM: Yes
|
||||
- Measurement Protocol: Yes
|
||||
|
||||
**Custom Dimension:** Yes (demo_type, industry, company_size as event-scoped)
|
||||
|
||||
**Frequency:** ~50-100 per month (estimated)
|
||||
|
||||
**Related Events:** demo_attend, demo_skip, upgrade_after_demo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Validation
|
||||
|
||||
### Type Validation
|
||||
|
||||
Ensure parameters use correct types:
|
||||
|
||||
```javascript
|
||||
// ✅ Correct types
|
||||
gtag('event', 'purchase', {
|
||||
'value': 99.99, // Number
|
||||
'currency': 'USD', // String
|
||||
'quantity': 3, // Integer
|
||||
'is_gift': true, // Boolean
|
||||
'items': [...] // Array
|
||||
});
|
||||
|
||||
// ❌ Wrong types
|
||||
gtag('event', 'purchase', {
|
||||
'value': '99.99', // String instead of number
|
||||
'quantity': '3', // String instead of integer
|
||||
'is_gift': 'true' // String instead of boolean
|
||||
});
|
||||
```
|
||||
|
||||
### Value Length Validation
|
||||
|
||||
```javascript
|
||||
// ✅ Good (under limits)
|
||||
gtag('event', 'form_submit', {
|
||||
'form_name': 'Contact Form', // OK (14 chars)
|
||||
'company_name': 'Acme Corp', // OK (9 chars)
|
||||
'email_body': 'Long message...' // OK (100 chars max)
|
||||
});
|
||||
|
||||
// ❌ Bad (too long)
|
||||
gtag('event', 'form_submit', {
|
||||
'form_description': '...[200 chars]...' // Exceeds 100 char limit
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complex Event Examples
|
||||
|
||||
### Example 1: Video Engagement Tracking
|
||||
|
||||
```javascript
|
||||
gtag('event', 'video_engagement', {
|
||||
'video_id': 'VID_2024_001',
|
||||
'video_title': 'GA4 Advanced Features',
|
||||
'video_duration': 1200, // seconds
|
||||
'time_watched': 845, // seconds
|
||||
'completion_percent': 70,
|
||||
'quality_selected': 'hd',
|
||||
'interaction_type': 'pause', // pause, seek, quality_change, fullscreen
|
||||
'engagement_score': 8 // 1-10 scale
|
||||
});
|
||||
```
|
||||
|
||||
### Example 2: SaaS Onboarding Tracking
|
||||
|
||||
```javascript
|
||||
gtag('event', 'onboarding_step_complete', {
|
||||
'step_number': 3,
|
||||
'step_name': 'team_invite',
|
||||
'total_steps': 5,
|
||||
'time_on_step': 245, // seconds
|
||||
'users_invited': 2,
|
||||
'skip_attempted': false
|
||||
});
|
||||
|
||||
// User-level context
|
||||
gtag('set', {
|
||||
'onboarding_progress': 'step_3_of_5',
|
||||
'onboarding_started_date': '2024-11-10'
|
||||
});
|
||||
```
|
||||
|
||||
### Example 3: E-commerce Product Discovery
|
||||
|
||||
```javascript
|
||||
gtag('event', 'product_discovery_complete', {
|
||||
'discovery_method': 'search', // search, recommendation, browse, email
|
||||
'search_term': 'running shoes',
|
||||
'filters_applied': 3,
|
||||
'results_viewed': 24,
|
||||
'products_liked': 5,
|
||||
'add_to_cart_from_discovery': 2
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event Consolidation vs Proliferation
|
||||
|
||||
### When to Consolidate Events
|
||||
|
||||
**Use one event with parameters instead of multiple events:**
|
||||
|
||||
```
|
||||
❌ Too many events:
|
||||
- button_click_signin
|
||||
- button_click_signup
|
||||
- button_click_demo
|
||||
- button_click_contact
|
||||
|
||||
✅ One consolidated event:
|
||||
- button_click (with parameter: button_name)
|
||||
```
|
||||
|
||||
### When to Separate Events
|
||||
|
||||
**Create separate events for distinct user journeys:**
|
||||
|
||||
```
|
||||
✅ Separate events (correct):
|
||||
- feature_explore (user discovered feature)
|
||||
- feature_adopt (user started using feature)
|
||||
- feature_abandon (user stopped using feature)
|
||||
|
||||
❌ One event (not enough context):
|
||||
- feature_interaction (ambiguous: explore or use?)
|
||||
```
|
||||
|
||||
### Decision Rule
|
||||
|
||||
- **Same event type + just different context**: Use parameters
|
||||
- **Different action sequences or business flows**: Use separate events
|
||||
- **Different analysis needs**: Consider separate events
|
||||
|
||||
---
|
||||
|
||||
## Event Limits and Quotas
|
||||
|
||||
### Standard GA4 Property Limits
|
||||
- Maximum event names: 500 event types per property
|
||||
- Maximum 25 parameters per event
|
||||
- Parameter name: 40 characters max
|
||||
- Parameter value: 100 characters max (with exceptions)
|
||||
|
||||
### Custom Dimension Limits
|
||||
- Event-scoped: 50 per property
|
||||
- User-scoped: 25 per property
|
||||
- Item-scoped: 10 per property
|
||||
|
||||
### Planning for Scale
|
||||
|
||||
When designing events, consider:
|
||||
1. How many unique custom events will be needed?
|
||||
2. What parameters will each event require?
|
||||
3. How many custom dimensions are needed?
|
||||
4. What's the growth trajectory?
|
||||
|
||||
If approaching limits, prioritize:
|
||||
- Events that directly impact business decisions
|
||||
- Parameters that enable key analyses
|
||||
- Dimensions needed for audience segmentation
|
||||
676
skills/ga4-custom-events/references/industry-patterns.md
Normal file
676
skills/ga4-custom-events/references/industry-patterns.md
Normal file
@@ -0,0 +1,676 @@
|
||||
# GA4 Custom Events: Industry-Specific Patterns
|
||||
|
||||
## Complete Industry Event Catalogs
|
||||
|
||||
### SaaS (Software as a Service)
|
||||
|
||||
#### Core SaaS Events
|
||||
|
||||
**Free Trial & Conversion:**
|
||||
```javascript
|
||||
// User initiates free trial
|
||||
gtag('event', 'trial_start', {
|
||||
'trial_plan': 'professional',
|
||||
'trial_days': 30,
|
||||
'utm_source': 'landing_page',
|
||||
'utm_medium': 'organic'
|
||||
});
|
||||
|
||||
// User converts trial to paid
|
||||
gtag('event', 'trial_conversion', {
|
||||
'trial_plan': 'professional',
|
||||
'paid_plan': 'professional_annual',
|
||||
'trial_duration_days': 28,
|
||||
'feature_used': 'advanced_reporting',
|
||||
'conversion_value': 999
|
||||
});
|
||||
|
||||
// User lets trial expire
|
||||
gtag('event', 'trial_expired', {
|
||||
'trial_plan': 'professional',
|
||||
'trial_days_used': 15,
|
||||
'reason_not_converted': 'price_too_high'
|
||||
});
|
||||
```
|
||||
|
||||
**Feature Adoption & Usage:**
|
||||
```javascript
|
||||
// User discovers premium feature
|
||||
gtag('event', 'feature_discover', {
|
||||
'feature_name': 'team_collaboration',
|
||||
'feature_category': 'premium',
|
||||
'discovery_source': 'help_article'
|
||||
});
|
||||
|
||||
// User activates premium feature
|
||||
gtag('event', 'feature_enable', {
|
||||
'feature_name': 'team_collaboration',
|
||||
'feature_category': 'premium',
|
||||
'current_plan': 'professional'
|
||||
});
|
||||
|
||||
// User first uses premium feature
|
||||
gtag('event', 'feature_first_use', {
|
||||
'feature_name': 'team_collaboration',
|
||||
'success': true,
|
||||
'time_to_first_use_hours': 3
|
||||
});
|
||||
```
|
||||
|
||||
**Account & Subscription Management:**
|
||||
```javascript
|
||||
// User upgrades subscription
|
||||
gtag('event', 'subscription_upgrade', {
|
||||
'from_plan': 'starter',
|
||||
'to_plan': 'professional',
|
||||
'upgrade_value': 50,
|
||||
'reason': 'more_users_needed'
|
||||
});
|
||||
|
||||
// User downgrades subscription
|
||||
gtag('event', 'subscription_downgrade', {
|
||||
'from_plan': 'professional',
|
||||
'to_plan': 'starter',
|
||||
'refund_amount': 50,
|
||||
'reason': 'reduced_usage'
|
||||
});
|
||||
|
||||
// User pauses/cancels subscription
|
||||
gtag('event', 'subscription_cancel', {
|
||||
'plan': 'professional',
|
||||
'tenure_months': 12,
|
||||
'reason': 'switching_competitor'
|
||||
});
|
||||
|
||||
// User adds team member/seat
|
||||
gtag('event', 'seat_add', {
|
||||
'current_seats': 5,
|
||||
'new_seats': 6,
|
||||
'cost_per_seat': 10
|
||||
});
|
||||
```
|
||||
|
||||
**Engagement & Activation:**
|
||||
```javascript
|
||||
// User invites teammate
|
||||
gtag('event', 'team_invite_send', {
|
||||
'invitees_count': 2,
|
||||
'email_domains': 'acme.com',
|
||||
'invite_source': 'onboarding'
|
||||
});
|
||||
|
||||
// User completes onboarding
|
||||
gtag('event', 'onboarding_complete', {
|
||||
'onboarding_duration_minutes': 45,
|
||||
'steps_completed': 8,
|
||||
'data_imported': true
|
||||
});
|
||||
|
||||
// User creates first report
|
||||
gtag('event', 'first_report_created', {
|
||||
'report_type': 'marketing_dashboard',
|
||||
'days_to_first_report': 2
|
||||
});
|
||||
```
|
||||
|
||||
**Integration & Extensions:**
|
||||
```javascript
|
||||
// User connects third-party integration
|
||||
gtag('event', 'integration_connect', {
|
||||
'integration_name': 'slack',
|
||||
'integration_category': 'communication',
|
||||
'reason': 'team_notifications'
|
||||
});
|
||||
|
||||
// User uses API
|
||||
gtag('event', 'api_first_call', {
|
||||
'api_endpoint': 'reports_v4',
|
||||
'api_auth_type': 'oauth',
|
||||
'user_type': 'developer'
|
||||
});
|
||||
```
|
||||
|
||||
**Support & Feedback:**
|
||||
```javascript
|
||||
// User opens support ticket
|
||||
gtag('event', 'support_ticket_open', {
|
||||
'ticket_category': 'billing',
|
||||
'severity': 'medium',
|
||||
'user_plan': 'professional'
|
||||
});
|
||||
|
||||
// User submits feature request
|
||||
gtag('event', 'feature_request_submit', {
|
||||
'requested_feature': 'custom_branding',
|
||||
'use_case': 'white_label_solution'
|
||||
});
|
||||
```
|
||||
|
||||
#### SaaS Event User Properties
|
||||
|
||||
Set these user-level properties:
|
||||
```javascript
|
||||
gtag('set', {
|
||||
'subscription_plan': 'professional',
|
||||
'subscription_status': 'active',
|
||||
'monthly_recurring_revenue': 99,
|
||||
'subscription_created_date': '2023-11-10',
|
||||
'user_role': 'admin',
|
||||
'company_size': 'mid-market',
|
||||
'use_case': 'marketing_analytics',
|
||||
'feature_tier': 'professional',
|
||||
'integration_count': 3,
|
||||
'onboarding_complete': true
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### E-commerce / Retail
|
||||
|
||||
#### Product Discovery Events
|
||||
|
||||
```javascript
|
||||
// User performs product search
|
||||
gtag('event', 'product_search', {
|
||||
'search_term': 'running shoes',
|
||||
'search_results_count': 45,
|
||||
'filters_applied': ['size', 'brand', 'price']
|
||||
});
|
||||
|
||||
// User applies product filter
|
||||
gtag('event', 'product_filter_apply', {
|
||||
'filter_type': 'price',
|
||||
'filter_value': '50-100',
|
||||
'filtered_results_count': 12
|
||||
});
|
||||
|
||||
// User views product list
|
||||
gtag('event', 'product_list_view', {
|
||||
'list_name': 'summer_sale',
|
||||
'list_category': 'apparel',
|
||||
'products_visible': 20
|
||||
});
|
||||
|
||||
// User reads product review
|
||||
gtag('event', 'product_review_view', {
|
||||
'product_id': 'SHOE_001',
|
||||
'review_rating': 4,
|
||||
'review_helpful_count': 15,
|
||||
'reviewer_verified_buyer': true
|
||||
});
|
||||
|
||||
// User submits product review
|
||||
gtag('event', 'product_review_submit', {
|
||||
'product_id': 'SHOE_001',
|
||||
'review_rating': 5,
|
||||
'review_length': 250,
|
||||
'includes_photo': true
|
||||
});
|
||||
```
|
||||
|
||||
#### Purchase Journey Events
|
||||
|
||||
```javascript
|
||||
// User views shopping cart
|
||||
gtag('event', 'cart_view', {
|
||||
'items_count': 3,
|
||||
'cart_value': 150,
|
||||
'last_modified_minutes_ago': 5
|
||||
});
|
||||
|
||||
// User updates cart
|
||||
gtag('event', 'cart_update', {
|
||||
'action': 'quantity_increase',
|
||||
'product_id': 'SHOE_001',
|
||||
'quantity_before': 1,
|
||||
'quantity_after': 2
|
||||
});
|
||||
|
||||
// User starts checkout
|
||||
gtag('event', 'checkout_start', {
|
||||
'items_count': 2,
|
||||
'cart_value': 99.98,
|
||||
'estimated_shipping': 10,
|
||||
'coupon_applied': false
|
||||
});
|
||||
|
||||
// User selects shipping method
|
||||
gtag('event', 'shipping_select', {
|
||||
'shipping_method': 'express',
|
||||
'shipping_cost': 25,
|
||||
'delivery_days': 2
|
||||
});
|
||||
|
||||
// User enters payment information
|
||||
gtag('event', 'payment_info_enter', {
|
||||
'payment_method': 'credit_card',
|
||||
'billing_same_as_shipping': true,
|
||||
'save_payment_method': true
|
||||
});
|
||||
|
||||
// User completes purchase
|
||||
gtag('event', 'purchase', {
|
||||
'transaction_id': 'TXN_12345',
|
||||
'value': 134.98,
|
||||
'currency': 'USD',
|
||||
'tax': 10.00,
|
||||
'shipping': 10.00,
|
||||
'coupon': 'SAVE10',
|
||||
'items': [
|
||||
{
|
||||
'item_id': 'SHOE_001',
|
||||
'item_name': 'Running Shoe Pro',
|
||||
'item_category': 'Apparel/Shoes',
|
||||
'item_brand': 'SportBrand',
|
||||
'item_variant': 'Blue/Size 10',
|
||||
'price': 119.99,
|
||||
'quantity': 1
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
#### Post-Purchase Events
|
||||
|
||||
```javascript
|
||||
// User initiates return
|
||||
gtag('event', 'return_initiate', {
|
||||
'transaction_id': 'TXN_12345',
|
||||
'return_item_count': 1,
|
||||
'return_reason': 'wrong_size'
|
||||
});
|
||||
|
||||
// User requests refund
|
||||
gtag('event', 'refund_request', {
|
||||
'transaction_id': 'TXN_12345',
|
||||
'refund_amount': 119.99,
|
||||
'refund_reason': 'product_quality'
|
||||
});
|
||||
|
||||
// User views order tracking
|
||||
gtag('event', 'order_tracking_view', {
|
||||
'transaction_id': 'TXN_12345',
|
||||
'days_since_order': 2,
|
||||
'current_status': 'in_transit'
|
||||
});
|
||||
|
||||
// User receives order
|
||||
gtag('event', 'order_delivered', {
|
||||
'transaction_id': 'TXN_12345',
|
||||
'delivery_time_days': 3
|
||||
});
|
||||
```
|
||||
|
||||
#### Loyalty & Engagement
|
||||
|
||||
```javascript
|
||||
// User joins loyalty program
|
||||
gtag('event', 'loyalty_join', {
|
||||
'program_name': 'VIP Rewards',
|
||||
'tier': 'silver',
|
||||
'referral_code': 'REF_ABC123'
|
||||
});
|
||||
|
||||
// User adds to wishlist
|
||||
gtag('event', 'wishlist_add', {
|
||||
'product_id': 'SHOE_002',
|
||||
'wishlist_size': 8,
|
||||
'item_in_stock': true
|
||||
});
|
||||
|
||||
// User receives and opens email
|
||||
gtag('event', 'email_campaign_open', {
|
||||
'campaign_name': 'summer_sale_2024',
|
||||
'email_type': 'promotional',
|
||||
'click_count': 2
|
||||
});
|
||||
|
||||
// User makes repeat purchase
|
||||
gtag('event', 'repeat_purchase', {
|
||||
'repeat_purchase_number': 3,
|
||||
'days_since_last_purchase': 45,
|
||||
'customer_lifetime_value': 450
|
||||
});
|
||||
```
|
||||
|
||||
#### E-commerce User Properties
|
||||
|
||||
```javascript
|
||||
gtag('set', {
|
||||
'customer_value': 'high',
|
||||
'purchase_frequency': 'monthly',
|
||||
'average_order_value': 150,
|
||||
'customer_lifetime_value': 3000,
|
||||
'loyalty_tier': 'gold',
|
||||
'preferred_category': 'shoes',
|
||||
'total_purchases': 15,
|
||||
'last_purchase_days_ago': 30
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Education / EdTech
|
||||
|
||||
#### Enrollment & Course Events
|
||||
|
||||
```javascript
|
||||
// User enrolls in course
|
||||
gtag('event', 'course_enroll', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'course_name': 'Python Fundamentals',
|
||||
'instructor_id': 'INSTR_001',
|
||||
'course_price': 99,
|
||||
'course_level': 'beginner',
|
||||
'payment_method': 'credit_card'
|
||||
});
|
||||
|
||||
// User starts course
|
||||
gtag('event', 'course_start', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'enrollment_to_start_days': 2
|
||||
});
|
||||
|
||||
// User completes course
|
||||
gtag('event', 'course_complete', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'course_completion_percent': 100,
|
||||
'days_to_completion': 30,
|
||||
'final_score': 92
|
||||
});
|
||||
|
||||
// User drops course
|
||||
gtag('event', 'course_drop', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'completion_percent': 45,
|
||||
'reason': 'time_constraints'
|
||||
});
|
||||
```
|
||||
|
||||
#### Learning Activities
|
||||
|
||||
```javascript
|
||||
// User completes lesson
|
||||
gtag('event', 'lesson_complete', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'lesson_id': 'LESSON_005',
|
||||
'lesson_title': 'Functions and Loops',
|
||||
'time_spent_minutes': 45,
|
||||
'lesson_completion_percent': 100
|
||||
});
|
||||
|
||||
// User takes quiz
|
||||
gtag('event', 'quiz_submit', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'quiz_id': 'QUIZ_003',
|
||||
'quiz_title': 'Module 3 Assessment',
|
||||
'questions_correct': 8,
|
||||
'questions_total': 10,
|
||||
'quiz_score_percent': 80,
|
||||
'attempts': 2
|
||||
});
|
||||
|
||||
// User watches video lesson
|
||||
gtag('event', 'video_watch', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'video_id': 'VID_001',
|
||||
'video_title': 'Introduction to Functions',
|
||||
'video_duration_minutes': 12,
|
||||
'watch_time_minutes': 11,
|
||||
'playback_speed': '1.0x',
|
||||
'replays': 1
|
||||
});
|
||||
|
||||
// User downloads resource
|
||||
gtag('event', 'course_resource_download', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'resource_type': 'code_files',
|
||||
'resource_size_mb': 25
|
||||
});
|
||||
|
||||
// User accesses discussion forum
|
||||
gtag('event', 'forum_post_create', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'post_category': 'technical_question',
|
||||
'post_upvotes_received': 3
|
||||
});
|
||||
```
|
||||
|
||||
#### Progress & Achievements
|
||||
|
||||
```javascript
|
||||
// User earns certificate
|
||||
gtag('event', 'certificate_earn', {
|
||||
'course_id': 'PYTHON_101',
|
||||
'certificate_type': 'completion',
|
||||
'final_grade': 'A',
|
||||
'certificate_sharable': true
|
||||
});
|
||||
|
||||
// User earns badge
|
||||
gtag('event', 'badge_unlock', {
|
||||
'badge_name': 'Quick Learner',
|
||||
'badge_type': 'achievement',
|
||||
'criteria_met': 'complete_3_courses_in_month'
|
||||
});
|
||||
|
||||
// User reaches learning milestone
|
||||
gtag('event', 'learning_milestone_reach', {
|
||||
'milestone_name': 'proficiency_level_2',
|
||||
'courses_completed': 3,
|
||||
'total_learning_hours': 50
|
||||
});
|
||||
```
|
||||
|
||||
#### Education User Properties
|
||||
|
||||
```javascript
|
||||
gtag('set', {
|
||||
'student_tier': 'premium',
|
||||
'total_courses_enrolled': 5,
|
||||
'total_courses_completed': 3,
|
||||
'average_course_rating': 4.5,
|
||||
'total_learning_hours': 50,
|
||||
'specialization': 'python_development',
|
||||
'progress_percent': 60
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Media & Publishing
|
||||
|
||||
#### Content Consumption
|
||||
|
||||
```javascript
|
||||
// User views article
|
||||
gtag('event', 'article_view', {
|
||||
'article_id': 'ART_001',
|
||||
'article_title': 'GA4 Complete Guide',
|
||||
'article_category': 'analytics',
|
||||
'article_author': 'John Doe',
|
||||
'publication_date': '2024-11-01',
|
||||
'article_word_count': 5000
|
||||
});
|
||||
|
||||
// User reaches scroll depth
|
||||
gtag('event', 'article_scroll_depth', {
|
||||
'article_id': 'ART_001',
|
||||
'scroll_depth_percent': 75,
|
||||
'time_on_page_seconds': 300
|
||||
});
|
||||
|
||||
// User reads entire article
|
||||
gtag('event', 'article_read_complete', {
|
||||
'article_id': 'ART_001',
|
||||
'reading_time_seconds': 1200,
|
||||
'estimated_reading_time_seconds': 1500
|
||||
});
|
||||
|
||||
// User watches video
|
||||
gtag('event', 'video_watch_start', {
|
||||
'video_id': 'VID_001',
|
||||
'video_title': 'GA4 Tutorial',
|
||||
'video_duration_minutes': 15,
|
||||
'video_category': 'tutorial'
|
||||
});
|
||||
|
||||
// User listens to podcast
|
||||
gtag('event', 'podcast_listen', {
|
||||
'episode_id': 'EP_123',
|
||||
'episode_title': 'Analytics Trends 2024',
|
||||
'listen_time_minutes': 45,
|
||||
'listened_to_end': true
|
||||
});
|
||||
```
|
||||
|
||||
#### Engagement & Interaction
|
||||
|
||||
```javascript
|
||||
// User shares content
|
||||
gtag('event', 'content_share', {
|
||||
'content_id': 'ART_001',
|
||||
'content_type': 'article',
|
||||
'share_method': 'email'
|
||||
});
|
||||
|
||||
// User comments on content
|
||||
gtag('event', 'content_comment', {
|
||||
'content_id': 'ART_001',
|
||||
'comment_length': 250,
|
||||
'comment_helpful_votes': 5
|
||||
});
|
||||
|
||||
// User likes/claps content
|
||||
gtag('event', 'content_engage', {
|
||||
'content_id': 'ART_001',
|
||||
'engagement_type': 'like',
|
||||
'engagement_value': 1
|
||||
});
|
||||
|
||||
// User bookmarks content
|
||||
gtag('event', 'content_bookmark', {
|
||||
'content_id': 'ART_001',
|
||||
'content_type': 'article',
|
||||
'bookmark_folder': 'analytics'
|
||||
});
|
||||
```
|
||||
|
||||
#### Subscription & Monetization
|
||||
|
||||
```javascript
|
||||
// User subscribes to newsletter
|
||||
gtag('event', 'newsletter_subscribe', {
|
||||
'newsletter_name': 'Weekly Analytics',
|
||||
'subscription_frequency': 'weekly',
|
||||
'email_validated': true
|
||||
});
|
||||
|
||||
// User upgrades to premium
|
||||
gtag('event', 'paywall_cross', {
|
||||
'article_id': 'ART_001',
|
||||
'paywall_type': 'subscription',
|
||||
'conversion_value': 99
|
||||
});
|
||||
|
||||
// User purchases single article
|
||||
gtag('event', 'article_purchase', {
|
||||
'article_id': 'ART_001',
|
||||
'purchase_price': 4.99,
|
||||
'currency': 'USD'
|
||||
});
|
||||
|
||||
// User renews subscription
|
||||
gtag('event', 'subscription_renew', {
|
||||
'subscription_plan': 'annual',
|
||||
'renewal_value': 99,
|
||||
'tenure_years': 2
|
||||
});
|
||||
```
|
||||
|
||||
#### Media User Properties
|
||||
|
||||
```javascript
|
||||
gtag('set', {
|
||||
'subscriber_status': 'premium',
|
||||
'subscription_plan': 'annual',
|
||||
'favorite_categories': 'analytics,marketing',
|
||||
'total_articles_read': 45,
|
||||
'average_article_completion': 0.75,
|
||||
'engagement_level': 'high'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cross-Industry Event Patterns
|
||||
|
||||
### Common to All Industries
|
||||
|
||||
**User Registration & Authentication:**
|
||||
```javascript
|
||||
gtag('event', 'user_signup', {
|
||||
'signup_method': 'email',
|
||||
'email_verified': true,
|
||||
'signup_source': 'landing_page'
|
||||
});
|
||||
|
||||
gtag('event', 'user_login', {
|
||||
'login_method': 'password',
|
||||
'login_platform': 'web'
|
||||
});
|
||||
```
|
||||
|
||||
**Account Management:**
|
||||
```javascript
|
||||
gtag('event', 'profile_update', {
|
||||
'fields_updated': 3,
|
||||
'completion_percent': 100
|
||||
});
|
||||
|
||||
gtag('event', 'preference_change', {
|
||||
'preference_type': 'language',
|
||||
'new_value': 'spanish'
|
||||
});
|
||||
```
|
||||
|
||||
**Help & Support:**
|
||||
```javascript
|
||||
gtag('event', 'help_search', {
|
||||
'search_query': 'how to export data',
|
||||
'results_count': 5
|
||||
});
|
||||
|
||||
gtag('event', 'live_chat_start', {
|
||||
'chat_topic': 'billing',
|
||||
'wait_time_seconds': 30
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Event Templates by Use Case
|
||||
|
||||
### Sign-Up Funnel Tracking
|
||||
```javascript
|
||||
gtag('event', 'signup_step_1_view'); // Email input
|
||||
gtag('event', 'signup_step_2_view'); // Password
|
||||
gtag('event', 'signup_complete'); // Confirmation
|
||||
```
|
||||
|
||||
### Feature Adoption Funnel
|
||||
```javascript
|
||||
gtag('event', 'feature_discover'); // See feature
|
||||
gtag('event', 'feature_enable'); // Turn on
|
||||
gtag('event', 'feature_use'); // Actually use
|
||||
gtag('event', 'feature_value_realize'); // Sees benefit
|
||||
```
|
||||
|
||||
### Conversion Value Path
|
||||
```javascript
|
||||
gtag('event', 'awareness_content_view'); // Top of funnel
|
||||
gtag('event', 'consideration_comparison'); // Middle
|
||||
gtag('event', 'decision_contact'); // Bottom
|
||||
gtag('event', 'purchase'); // Conversion
|
||||
```
|
||||
291
skills/ga4-custom-events/references/naming-conventions-guide.md
Normal file
291
skills/ga4-custom-events/references/naming-conventions-guide.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# GA4 Custom Event Naming Conventions Guide
|
||||
|
||||
## Complete Reference for Event Naming Strategy
|
||||
|
||||
### Core Naming Rules
|
||||
|
||||
**Rule 1: Use snake_case**
|
||||
```
|
||||
✅ Correct: video_tutorial_watched
|
||||
❌ Wrong: VideoTutorialWatched, video-tutorial-watched
|
||||
```
|
||||
|
||||
**Rule 2: Maximum 40 Characters**
|
||||
```
|
||||
✅ Correct: product_demo_request_submitted (34 chars)
|
||||
❌ Wrong: product_demo_request_submitted_by_enterprise_user (57 chars)
|
||||
```
|
||||
|
||||
**Rule 3: Start with Action Verb When Possible**
|
||||
```
|
||||
✅ Correct: file_downloaded, calculator_used, video_started
|
||||
❌ Wrong: download_file, use_calculator, start_video
|
||||
```
|
||||
|
||||
**Rule 4: Be Descriptive to Business Context**
|
||||
```
|
||||
✅ Correct: api_key_generated, subscription_upgraded, refund_requested
|
||||
❌ Wrong: action, event, click, data
|
||||
```
|
||||
|
||||
**Rule 5: Maintain Consistency**
|
||||
```
|
||||
✅ Correct: Always "video_completed" (never video_done, video_finished)
|
||||
❌ Wrong: Mixing "cart_abandoned", "cartAbandoned", "cart_left"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Naming Framework: [Action]_[Object]_[Context]
|
||||
|
||||
### Pattern Breakdown
|
||||
|
||||
**Action:** What the user is doing
|
||||
- `view`, `create`, `update`, `delete`, `submit`, `download`, `watch`, `listen`
|
||||
- `click`, `scroll`, `hover`, `focus`, `select`, `open`, `close`
|
||||
- `start`, `complete`, `pause`, `resume`, `skip`, `error`
|
||||
- `upgrade`, `downgrade`, `cancel`, `refund`, `return`
|
||||
|
||||
**Object:** What entity is being acted upon
|
||||
- `video`, `article`, `product`, `course`, `trial`, `subscription`
|
||||
- `button`, `form`, `popup`, `modal`, `notification`
|
||||
- `cart`, `order`, `invoice`, `receipt`, `account`
|
||||
- `dashboard`, `report`, `export`, `import`, `setting`
|
||||
|
||||
**Context:** Qualifier or category (optional)
|
||||
- `_free`, `_premium`, `_enterprise`, `_trial`
|
||||
- `_mobile`, `_desktop`, `_tablet`
|
||||
- `_email`, `_social`, `_referral`
|
||||
- `_failed`, `_success`, `_error`, `_retry`
|
||||
|
||||
### Framework Examples
|
||||
|
||||
**E-commerce Context:**
|
||||
- `product_view` (user viewed product)
|
||||
- `product_compare_viewed` (comparison page opened)
|
||||
- `add_to_cart` (item added to shopping cart)
|
||||
- `wishlist_add` (item added to saved list)
|
||||
- `review_submit` (product review posted)
|
||||
- `add_to_cart_mobile` (mobile-specific cart action)
|
||||
|
||||
**SaaS Context:**
|
||||
- `trial_start` (free trial activated)
|
||||
- `trial_end` (trial period ended)
|
||||
- `feature_unlock` (premium feature accessed)
|
||||
- `api_key_create` (authentication token generated)
|
||||
- `integration_connect` (third-party service linked)
|
||||
- `upgrade_click` (upgrade button clicked)
|
||||
|
||||
**Content Context:**
|
||||
- `video_start` (video playback began)
|
||||
- `video_complete` (video finished)
|
||||
- `article_read` (article fully read)
|
||||
- `podcast_skip` (podcast episode skipped)
|
||||
- `download_start` (resource download initiated)
|
||||
- `whitepaper_request` (lead magnet requested)
|
||||
|
||||
**SaaS Trial Context:**
|
||||
- `trial_signup` (trial account created)
|
||||
- `trial_feature_use` (premium feature tested during trial)
|
||||
- `trial_extend` (trial period extended)
|
||||
- `trial_convert` (trial upgraded to paid)
|
||||
- `trial_abandon` (trial left unused)
|
||||
|
||||
---
|
||||
|
||||
## Industry Naming Patterns
|
||||
|
||||
### SaaS Specific
|
||||
|
||||
**Onboarding & Activation:**
|
||||
- `onboarding_start`, `onboarding_step_completed`, `onboarding_complete`
|
||||
- `welcome_tour_start`, `tutorial_lesson_completed`
|
||||
- `first_data_entry`, `first_report_generated`
|
||||
|
||||
**Feature Engagement:**
|
||||
- `feature_discover`, `feature_explore`, `feature_adopt`
|
||||
- `integration_setup`, `integration_test`, `integration_activate`
|
||||
- `api_call_success`, `api_error_encountered`
|
||||
|
||||
**Monetization:**
|
||||
- `upgrade_modal_view`, `upgrade_click`, `upgrade_complete`
|
||||
- `plan_comparison_view`, `pricing_page_visit`
|
||||
- `subscription_pause`, `subscription_resume`, `subscription_cancel`
|
||||
|
||||
**Support & Help:**
|
||||
- `help_article_search`, `help_article_view`, `help_article_helpful`
|
||||
- `support_ticket_open`, `support_chat_start`
|
||||
- `feature_request_submit`, `bug_report_submit`
|
||||
|
||||
### Education Specific
|
||||
|
||||
**Course Engagement:**
|
||||
- `course_enroll`, `course_start`, `course_complete`
|
||||
- `lesson_view`, `lesson_complete`, `lesson_retry`
|
||||
- `quiz_attempt`, `quiz_submit`, `quiz_pass`
|
||||
|
||||
**Learning Progress:**
|
||||
- `certificate_earn`, `badge_unlock`, `milestone_reach`
|
||||
- `study_streak_start`, `study_streak_end`
|
||||
- `resource_download`, `note_create`, `bookmark_add`
|
||||
|
||||
**Social & Collaboration:**
|
||||
- `discussion_post_create`, `discussion_reply`, `discussion_helpful_vote`
|
||||
- `peer_review_submit`, `study_group_join`
|
||||
|
||||
### Media/Publishing Specific
|
||||
|
||||
**Content Consumption:**
|
||||
- `article_read_start`, `article_read_complete`, `article_scroll_depth_100`
|
||||
- `video_play`, `video_pause`, `video_quality_change`
|
||||
- `podcast_episode_start`, `podcast_episode_complete`
|
||||
|
||||
**Engagement:**
|
||||
- `content_share`, `content_comment`, `content_like`
|
||||
- `newsletter_signup`, `newsletter_open`, `newsletter_click`
|
||||
- `bookmark_save`, `bookmark_view`, `bookmark_share`
|
||||
|
||||
**Creator Features:**
|
||||
- `draft_create`, `article_publish`, `video_upload`
|
||||
- `monetization_enable`, `subscriber_only_content_publish`
|
||||
|
||||
### E-commerce Specific
|
||||
|
||||
**Product Discovery:**
|
||||
- `search_submit`, `filter_apply`, `sort_change`
|
||||
- `category_view`, `subcategory_view`
|
||||
- `product_review_read`, `product_rating_view`
|
||||
|
||||
**Purchase Journey:**
|
||||
- `product_add_to_cart`, `cart_view`, `cart_update_quantity`
|
||||
- `checkout_start`, `shipping_option_select`, `payment_method_select`
|
||||
- `apply_coupon`, `coupon_remove`, `gift_card_apply`
|
||||
|
||||
**Post-Purchase:**
|
||||
- `order_confirmation_view`, `order_tracking_click`
|
||||
- `product_return_initiate`, `refund_request_submit`
|
||||
|
||||
---
|
||||
|
||||
## Naming Checklist
|
||||
|
||||
Before finalizing custom event names:
|
||||
|
||||
- [ ] Event name is snake_case (lowercase, underscores)
|
||||
- [ ] Event name is under 40 characters
|
||||
- [ ] Event name is action-oriented (verb first when possible)
|
||||
- [ ] Event name is specific to business domain
|
||||
- [ ] Event name is consistent with existing naming scheme
|
||||
- [ ] Event name doesn't duplicate a recommended event
|
||||
- [ ] Event name would be understandable to business stakeholders
|
||||
- [ ] Event name could be documented without parameters
|
||||
- [ ] Event is documented in team's event dictionary
|
||||
- [ ] Event follows organization's naming standards
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern Examples to Avoid
|
||||
|
||||
### Generic Names
|
||||
```
|
||||
❌ Bad: click, event, action, user_action, interaction
|
||||
✅ Good: button_click, demo_button_click, video_play_click
|
||||
```
|
||||
|
||||
### Vague Names
|
||||
```
|
||||
❌ Bad: data, info, tracking, send, request
|
||||
✅ Good: customer_data_export, form_submit_request, contact_email_send
|
||||
```
|
||||
|
||||
### Inconsistent Names
|
||||
```
|
||||
❌ Bad: "user_signup", "registerUser", "SignUp", "sign_up_completed"
|
||||
✅ Good: Always "user_signup" (consistent)
|
||||
```
|
||||
|
||||
### Over-specific Names
|
||||
```
|
||||
❌ Bad: "user_clicked_blue_button_in_hero_section_on_homepage_on_mobile"
|
||||
✅ Good: "hero_cta_click" or "homepage_cta_click"
|
||||
```
|
||||
|
||||
### Parameters in Event Names
|
||||
```
|
||||
❌ Bad: "video_watched_hd", "trial_signup_30day", "user_from_google"
|
||||
✅ Good: "video_watched" with parameter video_quality="hd"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
### Example 1: Complete Naming Decision
|
||||
|
||||
**Business Goal:** Track when users request product demo
|
||||
|
||||
**Naming Process:**
|
||||
1. Action: request, submit
|
||||
2. Object: demo, product_demo
|
||||
3. Context: (none needed, already specific)
|
||||
|
||||
**Decision:** `demo_request` or `demo_request_submit`?
|
||||
- `demo_request` (28 chars) - Better: action is implied in "request"
|
||||
- Final name: `demo_request`
|
||||
|
||||
### Example 2: SaaS Feature Adoption
|
||||
|
||||
**Business Goal:** Track when users enable an integration
|
||||
|
||||
**Naming Process:**
|
||||
1. Action: enable, activate, setup, connect
|
||||
2. Object: integration
|
||||
3. Context: integration type
|
||||
|
||||
**Decision:** `integration_enabled` or `integration_setup_complete`?
|
||||
- `integration_enabled` (21 chars) - Clean and action-oriented
|
||||
- Final name: `integration_enabled`
|
||||
- Parameter: `integration_type` (e.g., "slack", "zapier")
|
||||
|
||||
### Example 3: Educational Progress
|
||||
|
||||
**Business Goal:** Track when students complete quiz
|
||||
|
||||
**Naming Process:**
|
||||
1. Action: submit, complete, finish
|
||||
2. Object: quiz
|
||||
3. Context: (optional: pass/fail)
|
||||
|
||||
**Decision:** `quiz_submit` or `quiz_complete`?
|
||||
- `quiz_submit` (11 chars) - User action
|
||||
- Final name: `quiz_submit`
|
||||
- Parameter: `quiz_result` (e.g., "pass", "fail"), `score` (numeric)
|
||||
|
||||
---
|
||||
|
||||
## Team Naming Standards (Template)
|
||||
|
||||
Document your organization's naming conventions:
|
||||
|
||||
```
|
||||
Event Naming Standard
|
||||
====================
|
||||
|
||||
Format: [action]_[object]_[context]
|
||||
|
||||
Actions Used: create, update, delete, view, submit, download, upload, upload, share
|
||||
Objects: product, course, video, article, user, team, workspace, settings
|
||||
Context: Optional qualifiers like _mobile, _free, _premium, _failed
|
||||
|
||||
Examples Approved:
|
||||
- product_view
|
||||
- course_enroll
|
||||
- quiz_submit
|
||||
- integration_enabled
|
||||
- support_ticket_create
|
||||
|
||||
Examples Not Approved:
|
||||
- user_action
|
||||
- click_event
|
||||
- data_send
|
||||
```
|
||||
598
skills/ga4-custom-events/references/parameter-strategy.md
Normal file
598
skills/ga4-custom-events/references/parameter-strategy.md
Normal file
@@ -0,0 +1,598 @@
|
||||
# GA4 Custom Event Parameters: Strategy & Best Practices
|
||||
|
||||
## Complete Parameter Planning Guide
|
||||
|
||||
### Parameter Selection Framework
|
||||
|
||||
#### Step 1: Identify Business Question
|
||||
|
||||
Define what the event reveals about user behavior:
|
||||
|
||||
```
|
||||
Event: demo_request
|
||||
Business Question: Which customer segments are interested in enterprise features?
|
||||
|
||||
Questions Answered:
|
||||
- Is the user currently a customer? (customer_status)
|
||||
- Which industry are they in? (industry)
|
||||
- What size is their company? (company_size)
|
||||
- How did they discover the demo? (discovery_source)
|
||||
```
|
||||
|
||||
#### Step 2: Determine Required Context
|
||||
|
||||
List parameters that answer each question:
|
||||
|
||||
| Question | Parameter | Values |
|
||||
|----------|-----------|--------|
|
||||
| Customer status? | customer_status | lead, current, competitor |
|
||||
| Industry? | industry | tech, finance, healthcare |
|
||||
| Company size? | company_size | startup, mid-market, enterprise |
|
||||
| Discovery source? | discovery_source | content, ad, partner, email |
|
||||
|
||||
#### Step 3: Validate Against Constraints
|
||||
|
||||
- Are there <25 parameters total?
|
||||
- Is each parameter <100 characters?
|
||||
- Can you analyze/segment by these parameters?
|
||||
- Will these parameters have <100,000 unique values?
|
||||
|
||||
#### Step 4: Implement & Register
|
||||
|
||||
Send parameters in event, then register as custom dimensions in GA4 Admin.
|
||||
|
||||
---
|
||||
|
||||
## Parameter Types & Guidelines
|
||||
|
||||
### String Parameters
|
||||
|
||||
Use for categorical data with limited unique values:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'feature_adopt', {
|
||||
'feature_name': 'advanced_reporting', // Good: ~10-50 unique
|
||||
'user_tier': 'professional', // Good: 5 options max
|
||||
'source': 'in_app_suggestion', // Good: ~10-20 options
|
||||
'user_email': 'john@example.com' // BAD: millions of unique
|
||||
});
|
||||
```
|
||||
|
||||
**String Parameter Best Practices:**
|
||||
- Keep values consistent (always lowercase: "free", not "Free")
|
||||
- Use labels, not IDs when possible
|
||||
- Avoid high-cardinality values (emails, URLs, timestamps)
|
||||
- Maximum 100 characters per value
|
||||
|
||||
### Numeric Parameters
|
||||
|
||||
Use for quantifiable measurements:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'video_watched', {
|
||||
'video_duration': 1200, // Seconds (integer)
|
||||
'watch_time': 900, // Seconds (integer)
|
||||
'completion_percent': 75, // Percentage (0-100)
|
||||
'playback_speed': 1.25, // Decimal (float)
|
||||
'rewatch_count': 2 // Count (integer)
|
||||
});
|
||||
```
|
||||
|
||||
**Numeric Parameter Best Practices:**
|
||||
- Use appropriate units (seconds, percentages, counts)
|
||||
- Store as numbers, not strings ("120", not "2 minutes")
|
||||
- Use integers for counts, floats for measurements
|
||||
- Document units clearly
|
||||
|
||||
### Boolean Parameters
|
||||
|
||||
Use for binary on/off state:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'signup_complete', {
|
||||
'email_verified': true,
|
||||
'payment_method_saved': false,
|
||||
'opted_to_newsletter': true
|
||||
});
|
||||
```
|
||||
|
||||
**Boolean Parameter Best Practices:**
|
||||
- Limited to true/false (or 0/1)
|
||||
- Clear naming (is_*, has_*, enabled_)
|
||||
- Only use when genuinely binary (not if 3+ states)
|
||||
|
||||
### Array/Object Parameters
|
||||
|
||||
Limited use - primarily for ecommerce items:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'purchase', {
|
||||
'items': [
|
||||
{
|
||||
'item_id': 'SKU_001',
|
||||
'item_name': 'Shoe Pro',
|
||||
'price': 99.99,
|
||||
'quantity': 1
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
**Array Parameter Best Practices:**
|
||||
- Only for items in ecommerce context
|
||||
- Each item = one product in transaction
|
||||
- Maximum 27 items per event
|
||||
|
||||
---
|
||||
|
||||
## Parameter Naming Strategy
|
||||
|
||||
### Naming Convention
|
||||
|
||||
Use lowercase snake_case, descriptive names:
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
gtag('event', 'feature_adopt', {
|
||||
'feature_name': 'advanced_reporting',
|
||||
'adoption_reason': 'team_request',
|
||||
'time_to_adopt_days': 3
|
||||
});
|
||||
|
||||
// ❌ Bad
|
||||
gtag('event', 'feature_adopt', {
|
||||
'fn': 'advanced_reporting', // Too abbreviated
|
||||
'reason': 'team_request', // Too generic
|
||||
'ttad': 3 // Unclear abbreviation
|
||||
});
|
||||
```
|
||||
|
||||
### Common Parameter Names
|
||||
|
||||
Establish naming standards:
|
||||
|
||||
| Concept | Parameter Name | Type |
|
||||
|---------|----------------|------|
|
||||
| User identifier | user_id, customer_id | string |
|
||||
| Product identifier | product_id, item_id | string |
|
||||
| Product name | product_name, item_name | string |
|
||||
| Category | category, item_category | string |
|
||||
| Price | price, unit_price | number |
|
||||
| Quantity | quantity | integer |
|
||||
| Status | status, account_status | string |
|
||||
| Type | type, event_type | string |
|
||||
| Method | method, payment_method | string |
|
||||
| Result | result, completion_status | string |
|
||||
| Reason | reason, error_reason | string |
|
||||
| Source | source, utm_source | string |
|
||||
| Duration | duration_minutes, duration_seconds | integer |
|
||||
| Percentage | percent_complete, completion_percent | integer |
|
||||
|
||||
### Avoid These Naming Patterns
|
||||
|
||||
```
|
||||
❌ Avoid abbreviations: ttc → time_to_convert
|
||||
❌ Avoid capitalization: ProductName → product_name
|
||||
❌ Avoid spaces: "product name" → product_name
|
||||
❌ Avoid special chars: product-name → product_name
|
||||
❌ Avoid encoding IDs: user_123 → user_id with value "123"
|
||||
❌ Avoid mixed naming: user_name + customerEmail → user_name + user_email
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Value Standards
|
||||
|
||||
### Categorical Values
|
||||
|
||||
```javascript
|
||||
// Plan types (be consistent)
|
||||
'plan': 'free' // ✅
|
||||
'plan': 'Free' // ❌ Inconsistent case
|
||||
'plan': 'starter' // ✅
|
||||
'plan': 'pro' // ✅
|
||||
'plan': 'PRO' // ❌ Inconsistent case
|
||||
|
||||
// Status values
|
||||
'status': 'active'
|
||||
'status': 'inactive'
|
||||
'status': 'pending'
|
||||
'status': 'suspended'
|
||||
```
|
||||
|
||||
### Time-Based Values
|
||||
|
||||
Never use timestamp parameters - use GA4 date automatically:
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong: timestamp parameters
|
||||
gtag('event', 'purchase', {
|
||||
'purchase_date': '2024-11-10', // NO - GA4 auto-collects
|
||||
'purchase_time': '14:30:00', // NO - Use event timestamp
|
||||
'user_since': 1728518400 // NO - Use user property registration
|
||||
});
|
||||
|
||||
// ✅ Right: relative time parameters
|
||||
gtag('event', 'purchase', {
|
||||
'customer_tenure_months': 12, // How long customer
|
||||
'time_to_purchase_days': 45, // Days from signup to purchase
|
||||
'session_duration_minutes': 15 // Duration in this session
|
||||
});
|
||||
```
|
||||
|
||||
### Numeric Value Ranges
|
||||
|
||||
Standardize ranges for analysis:
|
||||
|
||||
```javascript
|
||||
// ✅ Good: Consistent ranges
|
||||
'company_size': 'startup', // <50 people
|
||||
'company_size': 'mid-market', // 50-500 people
|
||||
'company_size': 'enterprise', // 500+ people
|
||||
|
||||
// ✅ Good: Specific numbers
|
||||
'team_size': 5,
|
||||
'message_length': 250,
|
||||
'files_uploaded': 3
|
||||
|
||||
// ❌ Bad: Vague or inconsistent
|
||||
'company_size': 'big', // Too vague
|
||||
'team_size': 'small', // Should be number
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Documentation Template
|
||||
|
||||
Document parameters for team reference:
|
||||
|
||||
```markdown
|
||||
## Event: demo_request
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Type | Values | Purpose | Scoping |
|
||||
|------|------|--------|---------|---------|
|
||||
| demo_type | string | walkthrough, technical, sales | Type of demo | Event |
|
||||
| industry | string | tech, finance, healthcare, etc. | User's industry | Event |
|
||||
| company_size | string | startup, mid-market, enterprise | Company size | Event |
|
||||
| current_customer | boolean | true, false | If already customer | Event |
|
||||
| utm_source | string | landing_page, email, referral | Traffic source | Event |
|
||||
|
||||
### Rules
|
||||
- demo_type is required
|
||||
- company_size defaults to "unknown" if not provided
|
||||
- Only send actual values, never null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Parameter Mistakes & Solutions
|
||||
|
||||
### Mistake 1: High-Cardinality Parameters
|
||||
|
||||
```javascript
|
||||
// ❌ Bad: Too many unique values
|
||||
gtag('event', 'lead_create', {
|
||||
'email': 'john@example.com', // Millions of values
|
||||
'company_domain': 'acme.com', // Thousands of values
|
||||
'full_name': 'John Smith', // Millions of values
|
||||
'phone': '555-1234' // Millions of values
|
||||
});
|
||||
|
||||
// ✅ Good: Categorical values
|
||||
gtag('event', 'lead_create', {
|
||||
'email_domain_category': 'company', // Few: company, gmail, other
|
||||
'company_size': 'enterprise', // Few: startup, mid, enterprise
|
||||
'lead_source': 'webinar', // Few: webinar, form, trial
|
||||
'country': 'US' // Manageable: country codes
|
||||
});
|
||||
```
|
||||
|
||||
### Mistake 2: Data That Belongs in Custom Dimensions, Not Parameters
|
||||
|
||||
```javascript
|
||||
// ❌ Bad: Event-specific data sent as parameter
|
||||
gtag('event', 'page_view', {
|
||||
'subscription_tier': 'premium' // This is user property
|
||||
});
|
||||
|
||||
// ✅ Good: Send as user property
|
||||
gtag('set', {
|
||||
'subscription_tier': 'premium'
|
||||
});
|
||||
|
||||
gtag('event', 'page_view');
|
||||
// subscription_tier automatically included
|
||||
```
|
||||
|
||||
### Mistake 3: Redundant Parameters
|
||||
|
||||
```javascript
|
||||
// ❌ Bad: Redundant - one is enough
|
||||
gtag('event', 'purchase', {
|
||||
'product_id': 'SHOE_001',
|
||||
'product_id_sku': 'SHOE_001',
|
||||
'sku': 'SHOE_001'
|
||||
});
|
||||
|
||||
// ✅ Good: Single consistent parameter
|
||||
gtag('event', 'purchase', {
|
||||
'product_id': 'SHOE_001'
|
||||
});
|
||||
```
|
||||
|
||||
### Mistake 4: Forgetting to Register Custom Dimensions
|
||||
|
||||
```javascript
|
||||
// ❌ Problem: Parameter sent but not registered
|
||||
gtag('event', 'video_watched', {
|
||||
'video_quality': 'hd' // Sent but not registered
|
||||
});
|
||||
|
||||
// ✅ Solution: Register in GA4 Admin
|
||||
// Admin > Data Display > Custom Definitions > Create Custom Dimension
|
||||
// Dimension Name: Video Quality
|
||||
// Scope: Event
|
||||
// Event Parameter: video_quality
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Implementation Patterns
|
||||
|
||||
### Pattern 1: Optional Parameters
|
||||
|
||||
Some parameters only relevant in certain conditions:
|
||||
|
||||
```javascript
|
||||
// Always sent
|
||||
gtag('event', 'purchase', {
|
||||
'value': 99.99,
|
||||
'currency': 'USD'
|
||||
});
|
||||
|
||||
// Only if coupon applied
|
||||
if (coupon_applied) {
|
||||
gtag('event', 'purchase', {
|
||||
'coupon': 'SAVE10',
|
||||
'discount_percent': 10
|
||||
});
|
||||
}
|
||||
|
||||
// Only for high-value orders
|
||||
if (value > 500) {
|
||||
gtag('event', 'purchase', {
|
||||
'high_value': true,
|
||||
'account_manager': 'jane_doe'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Conditional Parameter Values
|
||||
|
||||
Parameter value depends on other data:
|
||||
|
||||
```javascript
|
||||
// Determine user segment based on multiple factors
|
||||
let customer_segment;
|
||||
if (annual_spend > 50000) {
|
||||
customer_segment = 'vip';
|
||||
} else if (annual_spend > 10000) {
|
||||
customer_segment = 'high_value';
|
||||
} else if (annual_spend > 1000) {
|
||||
customer_segment = 'regular';
|
||||
} else {
|
||||
customer_segment = 'free';
|
||||
}
|
||||
|
||||
gtag('event', 'purchase', {
|
||||
'customer_segment': customer_segment,
|
||||
'annual_spend': annual_spend
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 3: Enriched Parameters
|
||||
|
||||
Add context by combining data sources:
|
||||
|
||||
```javascript
|
||||
// Combine internal + external data
|
||||
let utm_source = getURLParameter('utm_source');
|
||||
let internal_campaign = getCampaignIdFromDB();
|
||||
let marketing_channel = mapToChannel(utm_source);
|
||||
|
||||
gtag('event', 'signup_complete', {
|
||||
'utm_source': utm_source, // Raw UTM param
|
||||
'internal_campaign': internal_campaign, // From DB
|
||||
'marketing_channel': marketing_channel // Derived/mapped
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Parameter Strategies
|
||||
|
||||
### Strategy 1: Parameter Hierarchies
|
||||
|
||||
Use structured parameter naming for related data:
|
||||
|
||||
```javascript
|
||||
gtag('event', 'course_complete', {
|
||||
// Hierarchical structure
|
||||
'course_type': 'technical', // Top level
|
||||
'course_subtype': 'python', // Sub-category
|
||||
'course_level': 'advanced', // Difficulty
|
||||
|
||||
// Alternative: use compound parameter
|
||||
'course_id': 'PYTHON_ADV_101' // ID encodes all info
|
||||
});
|
||||
```
|
||||
|
||||
### Strategy 2: Derived Parameters
|
||||
|
||||
Calculate parameters from other data:
|
||||
|
||||
```javascript
|
||||
let time_to_purchase_days = (purchase_date - signup_date) / 86400000;
|
||||
let engagement_score = calculate_engagement(activity_log);
|
||||
let ltv_segment = classify_ltv(revenue_history);
|
||||
|
||||
gtag('event', 'first_purchase', {
|
||||
'time_to_purchase_days': Math.round(time_to_purchase_days),
|
||||
'engagement_score': engagement_score, // 1-10
|
||||
'ltv_segment': ltv_segment // low, medium, high
|
||||
});
|
||||
```
|
||||
|
||||
### Strategy 3: Lookup Table Parameters
|
||||
|
||||
Map internal IDs to user-friendly values:
|
||||
|
||||
```javascript
|
||||
// Define mapping
|
||||
const feature_lookup = {
|
||||
'feat_001': 'team_collaboration',
|
||||
'feat_002': 'advanced_reporting',
|
||||
'feat_003': 'api_access'
|
||||
};
|
||||
|
||||
let feature_id = getUserFeature();
|
||||
gtag('event', 'feature_enable', {
|
||||
'feature_name': feature_lookup[feature_id], // User-friendly
|
||||
'feature_id': feature_id // Internal ID
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Limits & Optimization
|
||||
|
||||
### GA4 Parameter Limits
|
||||
|
||||
| Limit | Value |
|
||||
|-------|-------|
|
||||
| Parameters per event | 25 maximum |
|
||||
| Parameter name length | 40 characters maximum |
|
||||
| Parameter value length | 100 characters maximum* |
|
||||
| Custom dimension parameters | 50 event-scoped, 25 user-scoped |
|
||||
|
||||
*Exceptions: page_title (300), page_referrer (420), page_location (1000)
|
||||
|
||||
### Optimization Strategies
|
||||
|
||||
**When at Parameter Limit:**
|
||||
|
||||
1. **Consolidate Related Parameters:**
|
||||
```javascript
|
||||
// ❌ Many parameters
|
||||
gtag('event', 'purchase', {
|
||||
'item_1_name': 'Shoe',
|
||||
'item_1_price': 99.99,
|
||||
'item_2_name': 'Shirt',
|
||||
'item_2_price': 29.99
|
||||
});
|
||||
|
||||
// ✅ Use items array
|
||||
gtag('event', 'purchase', {
|
||||
'items': [
|
||||
{ 'item_name': 'Shoe', 'price': 99.99 },
|
||||
{ 'item_name': 'Shirt', 'price': 29.99 }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
2. **Use Compound Values:**
|
||||
```javascript
|
||||
// ❌ Many parameters
|
||||
gtag('event', 'feature_adopt', {
|
||||
'feature_category': 'reporting',
|
||||
'feature_type': 'advanced',
|
||||
'feature_difficulty': 'high'
|
||||
});
|
||||
|
||||
// ✅ Compound value
|
||||
gtag('event', 'feature_adopt', {
|
||||
'feature_class': 'reporting_advanced_high'
|
||||
});
|
||||
```
|
||||
|
||||
3. **Move to User Properties:**
|
||||
```javascript
|
||||
// ❌ Repeat in every event
|
||||
gtag('event', 'purchase', {
|
||||
'company_size': 'enterprise',
|
||||
'industry': 'finance'
|
||||
});
|
||||
|
||||
// ✅ Set as user property
|
||||
gtag('set', {
|
||||
'company_size': 'enterprise',
|
||||
'industry': 'finance'
|
||||
});
|
||||
|
||||
gtag('event', 'purchase', {
|
||||
// user properties automatically included
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parameter Testing & Validation
|
||||
|
||||
### Test Checklist
|
||||
|
||||
Before production deployment:
|
||||
|
||||
- [ ] All parameters sent are documented
|
||||
- [ ] Parameter values are consistent (same values always)
|
||||
- [ ] High-cardinality parameters removed or consolidated
|
||||
- [ ] No PII (personally identifiable information) in parameters
|
||||
- [ ] Parameter values don't exceed 100 characters
|
||||
- [ ] Total parameters per event ≤25
|
||||
- [ ] Custom dimensions registered in GA4 Admin
|
||||
- [ ] DebugView shows correct parameter values
|
||||
- [ ] 24-48 hours passed - data appears in reports
|
||||
- [ ] Custom dimension values appear in report breakdowns
|
||||
|
||||
### Validation Script (JavaScript)
|
||||
|
||||
```javascript
|
||||
function validateEventParameters(eventName, params) {
|
||||
// Check parameter count
|
||||
if (Object.keys(params).length > 25) {
|
||||
console.warn(`Event ${eventName} has >25 parameters`);
|
||||
}
|
||||
|
||||
// Check parameter name length
|
||||
Object.keys(params).forEach(key => {
|
||||
if (key.length > 40) {
|
||||
console.warn(`Parameter name "${key}" exceeds 40 chars`);
|
||||
}
|
||||
|
||||
// Check parameter value length
|
||||
const value = params[key];
|
||||
if (typeof value === 'string' && value.length > 100) {
|
||||
console.warn(`Parameter "${key}" value exceeds 100 chars`);
|
||||
}
|
||||
|
||||
// Check for PII patterns
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.includes('@') && value.includes('.')) {
|
||||
console.warn(`Parameter "${key}" may contain email`);
|
||||
}
|
||||
if (/\b\d{3}-\d{2}-\d{4}\b/.test(value)) {
|
||||
console.warn(`Parameter "${key}" may contain SSN`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Usage
|
||||
validateEventParameters('purchase', {
|
||||
'value': 99.99,
|
||||
'currency': 'USD'
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user