381 lines
8.4 KiB
Markdown
381 lines
8.4 KiB
Markdown
# GTM Best Practices for GA4 Implementation
|
|
|
|
## Container Organization
|
|
|
|
### Naming Conventions
|
|
|
|
**Tags:**
|
|
- `GA4 - Configuration` (base tag)
|
|
- `GA4 - [Event Name] - [Description]`
|
|
- Examples:
|
|
- `GA4 - Button Click - Subscribe`
|
|
- `GA4 - Form Submit - Contact`
|
|
- `GA4 - Purchase`
|
|
|
|
**Triggers:**
|
|
- `[Type] - [Description]`
|
|
- Examples:
|
|
- `All Pages`
|
|
- `Click - Subscribe Button`
|
|
- `Form - Contact Submit`
|
|
- `Custom Event - add_to_cart`
|
|
|
|
**Variables:**
|
|
- `DL - [Name]` for Data Layer Variables
|
|
- `JS - [Name]` for JavaScript Variables
|
|
- `Cookie - [Name]` for Cookies
|
|
- Examples:
|
|
- `DL - Product ID`
|
|
- `JS - User Tier`
|
|
- `Cookie - Session ID`
|
|
|
|
### Folder Structure
|
|
|
|
**Organize by Function:**
|
|
```
|
|
Folders:
|
|
├── GA4 - Core (Configuration tag)
|
|
├── GA4 - E-commerce (Purchase, add_to_cart, etc.)
|
|
├── GA4 - Engagement (Scroll, clicks, video)
|
|
├── GA4 - Forms
|
|
└── Variables - Data Layer
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Rule 1: Use One GA4 Configuration Tag
|
|
|
|
**CORRECT:**
|
|
- One GA4 Configuration tag
|
|
- Fires on Initialization - All Pages
|
|
- Multiple event tags reference same Measurement ID
|
|
|
|
**WRONG:**
|
|
- Multiple GA4 Configuration tags
|
|
- Different Measurement IDs in same container
|
|
|
|
### Rule 2: Minimize Data Layer Pushes
|
|
|
|
**CORRECT:**
|
|
```javascript
|
|
dataLayer.push({
|
|
'event': 'add_to_cart',
|
|
'product_id': 'SKU_123',
|
|
'product_name': 'Blue T-Shirt',
|
|
'price': 29.99
|
|
});
|
|
```
|
|
|
|
**WRONG:**
|
|
```javascript
|
|
dataLayer.push({'event': 'add_to_cart'});
|
|
dataLayer.push({'product_id': 'SKU_123'});
|
|
dataLayer.push({'product_name': 'Blue T-Shirt'});
|
|
dataLayer.push({'price': 29.99});
|
|
```
|
|
|
|
### Rule 3: Use Specific Triggers
|
|
|
|
**CORRECT:**
|
|
```
|
|
Trigger Type: Click - All Elements
|
|
Fires on: Some Clicks
|
|
Condition: Click ID equals subscribe-btn
|
|
```
|
|
|
|
**WRONG:**
|
|
```
|
|
Trigger Type: Click - All Elements
|
|
Fires on: All Clicks
|
|
(Then filter in tag)
|
|
```
|
|
|
|
### Rule 4: Batch Event Parameters
|
|
|
|
**CORRECT:**
|
|
- Add all parameters in single event tag
|
|
- Use data layer variables efficiently
|
|
|
|
**WRONG:**
|
|
- Multiple tags for same event
|
|
- Separate pushes for each parameter
|
|
|
|
## Data Quality
|
|
|
|
### Use Consistent Event Names
|
|
|
|
**Best Practice:**
|
|
- Follow GA4 recommended event names
|
|
- Use snake_case consistently
|
|
- Document custom events
|
|
|
|
**Recommended Events:**
|
|
- `purchase`, `add_to_cart`, `begin_checkout`
|
|
- `sign_up`, `login`, `generate_lead`
|
|
- `view_item`, `view_item_list`, `select_item`
|
|
|
|
**Custom Events:**
|
|
- `button_click`, `form_submit`, `video_start`
|
|
- Keep lowercase with underscores
|
|
- Limit to 40 characters
|
|
|
|
### Validate Data Types
|
|
|
|
**Correct Types:**
|
|
- **String:** Event names, IDs, categories
|
|
- **Number:** Values, prices, quantities, revenue
|
|
- **Boolean:** true/false (not "true"/"false")
|
|
- **Array:** Items array for e-commerce
|
|
|
|
**Example:**
|
|
```javascript
|
|
dataLayer.push({
|
|
'event': 'purchase',
|
|
'transaction_id': 'T_12345', // String
|
|
'value': 99.99, // Number
|
|
'currency': 'USD', // String
|
|
'items': [...] // Array
|
|
});
|
|
```
|
|
|
|
### Avoid PII (Personally Identifiable Information)
|
|
|
|
**DO NOT Track:**
|
|
- Email addresses
|
|
- Phone numbers
|
|
- Full names
|
|
- Street addresses
|
|
- Social Security numbers
|
|
|
|
**Safe to Track:**
|
|
- Email domain (`@gmail.com`, `@company.com`)
|
|
- User IDs (hashed/anonymized)
|
|
- City/region (not specific addresses)
|
|
|
|
## Testing and QA
|
|
|
|
### Pre-Publication Checklist
|
|
|
|
- [ ] All tags fire in Preview mode
|
|
- [ ] Variables populate correctly
|
|
- [ ] Event names follow naming conventions
|
|
- [ ] Parameters have correct data types
|
|
- [ ] No PII in parameters
|
|
- [ ] Events appear in GA4 DebugView
|
|
- [ ] No duplicate events
|
|
- [ ] Data layer formatted correctly
|
|
- [ ] Documentation updated
|
|
- [ ] Version description written
|
|
|
|
### Version Control
|
|
|
|
**Every Publish:**
|
|
1. **Version Name:** Descriptive (e.g., "GA4 E-commerce Tracking - Nov 2024")
|
|
2. **Version Description:** Detailed list of changes
|
|
3. **Workspace Name:** Feature/task description
|
|
|
|
**Example Version Description:**
|
|
```
|
|
Changes:
|
|
- Added GA4 purchase event tracking
|
|
- Created data layer variables for transaction data
|
|
- Added form submission tracking for contact form
|
|
- Fixed duplicate page_view issue
|
|
|
|
Tested:
|
|
- Purchase flow (3 test transactions)
|
|
- Contact form submission
|
|
- All pages for duplicate events
|
|
```
|
|
|
|
### Rollback Plan
|
|
|
|
**If Issues After Publishing:**
|
|
1. Go to **Versions** tab
|
|
2. Find previous working version
|
|
3. Click **"•••"** → **Publish**
|
|
4. Confirm rollback
|
|
|
|
**Prevention:**
|
|
- Test thoroughly before publishing
|
|
- Publish during low-traffic periods
|
|
- Monitor for 30+ minutes after publishing
|
|
|
|
## Security and Privacy
|
|
|
|
### Use Appropriate Consent Mode
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
// Set default consent state
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
|
|
gtag('consent', 'default', {
|
|
'ad_storage': 'denied',
|
|
'analytics_storage': 'denied'
|
|
});
|
|
|
|
// Update after user consent
|
|
gtag('consent', 'update', {
|
|
'ad_storage': 'granted',
|
|
'analytics_storage': 'granted'
|
|
});
|
|
```
|
|
|
|
### IP Anonymization
|
|
|
|
**Not Needed for GA4:**
|
|
- GA4 does not log or store IP addresses by default
|
|
- No `anonymize_ip` parameter needed
|
|
|
|
### Data Retention Settings
|
|
|
|
**In GA4:**
|
|
1. Admin → Data Settings → Data Retention
|
|
2. Set event data retention (2 or 14 months)
|
|
3. Enable/disable "Reset user data on new activity"
|
|
|
|
## Maintenance
|
|
|
|
### Regular Audits
|
|
|
|
**Monthly:**
|
|
- Review Tags Not Fired section
|
|
- Check for broken triggers
|
|
- Verify variables still working
|
|
- Test critical paths
|
|
|
|
**Quarterly:**
|
|
- Full container audit
|
|
- Remove unused tags/triggers/variables
|
|
- Update documentation
|
|
- Review naming conventions
|
|
|
|
**Annually:**
|
|
- Major cleanup
|
|
- Archive old tags
|
|
- Update to latest GA4 features
|
|
|
|
### Documentation
|
|
|
|
**Maintain Documentation For:**
|
|
- All custom events and parameters
|
|
- Data layer structure
|
|
- Trigger logic
|
|
- Variable mappings
|
|
- Change history
|
|
|
|
**Example Documentation:**
|
|
```markdown
|
|
# Custom Events
|
|
|
|
## add_to_cart
|
|
Fires when: User clicks "Add to Cart" button
|
|
Parameters:
|
|
- product_id (string): SKU
|
|
- product_name (string): Product name
|
|
- price (number): Unit price
|
|
- quantity (number): Quantity added
|
|
Data Layer: dataLayer.push({event: 'add_to_cart', ...})
|
|
GTM Tag: GA4 - Add to Cart
|
|
Trigger: Custom Event - add_to_cart
|
|
```
|
|
|
|
## Common Pitfalls to Avoid
|
|
|
|
### Pitfall 1: Multiple Measurement IDs
|
|
|
|
**Issue:** Using different Measurement IDs in event tags
|
|
**Solution:** Use same Measurement ID across all GA4 tags
|
|
|
|
### Pitfall 2: Not Clearing ecommerce Object
|
|
|
|
**Issue:** Old ecommerce data persists across events
|
|
**Solution:** Clear before each ecommerce push
|
|
```javascript
|
|
dataLayer.push({ ecommerce: null });
|
|
dataLayer.push({ event: 'purchase', ecommerce: {...} });
|
|
```
|
|
|
|
### Pitfall 3: Tracking PII
|
|
|
|
**Issue:** Accidentally sending email addresses or names
|
|
**Solution:** Review all data layer pushes and parameters
|
|
|
|
### Pitfall 4: Inconsistent Event Names
|
|
|
|
**Issue:** `buttonClick`, `button_click`, `Button_Click`
|
|
**Solution:** Use lowercase with underscores consistently
|
|
|
|
### Pitfall 5: Publishing Without Testing
|
|
|
|
**Issue:** Broken tags go live
|
|
**Solution:** Always use Preview mode before publishing
|
|
|
|
### Pitfall 6: No Version Descriptions
|
|
|
|
**Issue:** Can't identify what changed in version history
|
|
**Solution:** Write detailed version descriptions
|
|
|
|
### Pitfall 7: Overcomplicating Setup
|
|
|
|
**Issue:** Too many tags for simple tracking
|
|
**Solution:** Start simple, add complexity as needed
|
|
|
|
## Migration from gtag.js to GTM
|
|
|
|
**Steps:**
|
|
|
|
1. **Remove gtag.js snippet** from website
|
|
2. **Install GTM container** code
|
|
3. **Create GA4 Configuration tag** in GTM
|
|
4. **Migrate custom events** to GTM event tags
|
|
5. **Test thoroughly** in Preview mode
|
|
6. **Publish** GTM container
|
|
|
|
**Benefits:**
|
|
- No code changes needed for new tracking
|
|
- Easier testing with Preview mode
|
|
- Centralized tag management
|
|
- Better collaboration
|
|
|
|
## Collaboration Best Practices
|
|
|
|
### Workspaces
|
|
|
|
**Use Workspaces For:**
|
|
- Feature development
|
|
- Testing major changes
|
|
- Team collaboration
|
|
|
|
**Best Practices:**
|
|
- One workspace per feature/task
|
|
- Descriptive workspace names
|
|
- Merge/publish when complete
|
|
|
|
### User Permissions
|
|
|
|
**Roles:**
|
|
- **No Access:** No access to container
|
|
- **Read:** View-only access
|
|
- **Edit:** Create/modify tags (can't publish)
|
|
- **Approve:** Publish changes
|
|
- **Publish:** Full control
|
|
|
|
**Best Practice:**
|
|
- Limit Publish access
|
|
- Most users: Edit access
|
|
- Senior team: Approve/Publish
|
|
|
|
### Change Management
|
|
|
|
**Process:**
|
|
1. Create workspace for change
|
|
2. Implement and test
|
|
3. Submit for review (if applicable)
|
|
4. Peer review changes
|
|
5. Final testing in Preview
|
|
6. Publish with detailed description
|
|
7. Monitor for 30+ minutes
|