# Form Tracking Best Practices

**Last Updated:** 2025-11-28

Best practices for implementing and maintaining GTM form tracking across the Ordio website.

## Naming Conventions

### Form Names

**Format:** `[Purpose] [Type] [Location/Context]`

**Examples:**

- `Main Lead Capture Form`
- `SDR Lead Capture Form`
- `Gated Content Form`
- `Template Download - Schichtplan`
- `Lead Capture Popup - Step 1`
- `Webinar Registration - Product`

**Guidelines:**

- Use title case
- Be descriptive but concise
- Include context when form ID is reused (e.g., "Demo Form (Main)" vs "Demo Form (SDR)")
- Use consistent terminology across all forms

### GTM Tracking IDs

**Format:** `[form-type]-[location/context]`

**Examples:**

- `demo-form-main`
- `demo-form-sdr`
- `content-form`
- `email-form-hero`
- `hubspot-sdr-form`
- `api-lead-capture`
- `api-submit-template`

**Guidelines:**

- Use kebab-case (lowercase with hyphens)
- Be unique across all forms
- Include location/context for duplicate form IDs
- Match form purpose in name

### DataLayer Variable Names

**Format:** Use descriptive, lowercase_with_underscores

**Standard Variables:**

- `form_id`
- `form_name`
- `form_type`
- `hubspot_form_guid`
- `page_url`
- `utm_source`
- `utm_campaign`

**Custom Variables:**

- `form_step` (for multi-step forms)
- `content_type` (for gated content)
- `template_type` (for template downloads)
- `addon_name` (for addon forms)
- `webinar_type` (for webinar forms)

## DataLayer Structure Standards

### Required Fields

All form submission events must include:

```javascript
{
  event: 'form_submit', // Always 'form_submit'
  form_id: 'form-id', // Form element ID or unique identifier
  form_name: 'Form Name', // Descriptive form name
  form_type: 'custom_html' | 'hubspot_embedded' | 'api_based',
  page_url: window.location.href, // Full page URL
  page_title: document.title, // Page title
  page_path: window.location.pathname // Page path
}
```

### Optional but Recommended Fields

```javascript
{
  hubspot_form_guid: 'form-guid', // HubSpot form GUID if applicable
  utm_source: 'source', // UTM source
  utm_medium: 'medium', // UTM medium
  utm_campaign: 'campaign', // UTM campaign
  utm_term: 'term', // UTM term
  utm_content: 'content', // UTM content
  gclid: 'gclid', // Google Click ID
  lead_source: 'source', // Lead source
  partner: 'partner' // Partner identifier
}
```

### Form-Specific Fields

Add form-specific fields via `additionalData`:

```javascript
// Multi-step forms
form_step: 1;

// Gated content
content_type: "whitepaper";

// Template downloads
template_type: "Schichtplan Vorlage - Template";

// Addon forms
addon_name: "Payroll";

// Webinar forms
webinar_type: "product_webinar";
```

## Error Handling Patterns

### Always Check for Utility Existence

```javascript
// ✅ CORRECT
if (
  window.GTMFormTracker &&
  typeof window.GTMFormTracker.trackCustomHTMLForm === "function"
) {
  window.GTMFormTracker.trackCustomHTMLForm(form);
}

// ❌ WRONG - May throw error if utility not loaded
window.GTMFormTracker.trackCustomHTMLForm(form);
```

### Never Block Form Submission

```javascript
// ✅ CORRECT - Track before submit, don't wait
if (window.GTMFormTracker) {
  window.GTMFormTracker.trackCustomHTMLForm(form);
}
form.submit(); // Form submits immediately

// ❌ WRONG - Waiting for tracking could delay submission
if (window.GTMFormTracker) {
  await window.GTMFormTracker.trackCustomHTMLForm(form);
}
form.submit();
```

### Handle Errors Gracefully

The tracking utility handles errors internally. You don't need try-catch blocks:

```javascript
// ✅ CORRECT - Utility handles errors
if (window.GTMFormTracker) {
  window.GTMFormTracker.trackCustomHTMLForm(form);
}

// ❌ UNNECESSARY - Utility already handles errors
try {
  if (window.GTMFormTracker) {
    window.GTMFormTracker.trackCustomHTMLForm(form);
  }
} catch (e) {
  // Not needed - utility handles this
}
```

## Performance Considerations

### Defer Script Loading

The tracking utility is loaded with `defer` attribute:

```html
<script src="/v2/js/gtm-form-tracking.js?v=..." defer></script>
```

**Why:** Ensures script doesn't block page rendering.

### Minimize DataLayer Payload

Only include necessary data:

```javascript
// ✅ GOOD - Only essential data
{
  event: 'form_submit',
  form_id: 'demo-form',
  form_name: 'Main Lead Capture',
  form_type: 'custom_html'
}

// ❌ BAD - Unnecessary data
{
  event: 'form_submit',
  form_id: 'demo-form',
  form_name: 'Main Lead Capture',
  form_type: 'custom_html',
  user_agent: navigator.userAgent, // Not needed
  screen_resolution: screen.width + 'x' + screen.height, // Not needed
  timestamp: Date.now(), // GTM adds this automatically
  // ... many more unnecessary fields
}
```

### Track Before Submission

Track immediately before form submission to minimize delay:

```javascript
// ✅ GOOD - Track right before submit
if (window.GTMFormTracker) {
  window.GTMFormTracker.trackCustomHTMLForm(form);
}
form.submit();

// ❌ BAD - Track too early (data might change)
if (window.GTMFormTracker) {
  window.GTMFormTracker.trackCustomHTMLForm(form);
}
// ... other code that might modify form ...
form.submit();
```

## Code Organization

### Centralized Tracking Utility

**Always use the centralized utility:** `window.GTMFormTracker`

**Don't create custom dataLayer pushes** - use the utility functions:

```javascript
// ✅ GOOD - Use utility
window.GTMFormTracker.trackCustomHTMLForm(form, options);

// ❌ BAD - Custom dataLayer push (inconsistent structure)
window.dataLayer.push({
  event: "form_submit",
  // ... inconsistent structure
});
```

### Consistent Implementation

Use the same pattern across all forms:

```javascript
// Standard pattern for custom HTML forms
if (
  window.GTMFormTracker &&
  typeof window.GTMFormTracker.trackCustomHTMLForm === "function"
) {
  window.GTMFormTracker.trackCustomHTMLForm(formElement, {
    formName: "Form Name",
    formType: "form_subtype",
    hubspotFormGuid: "guid-here",
  });
}
```

## Testing Best Practices

### Test in GTM Preview Mode

Always test in GTM Preview mode before deploying:

1. Open GTM Preview mode
2. Navigate to page with form
3. Submit form
4. Verify:
   - Trigger fires
   - DataLayer push appears
   - Variables populate
   - GA4 tag fires

### Test Across Browsers

Test on:

- Chrome (desktop and mobile)
- Firefox
- Safari (desktop and mobile)
- Edge

### Test Edge Cases

- Form validation failures (tracking should still fire)
- Network errors (form submission may fail, but tracking should fire)
- Ad blockers (tracking may be blocked - document limitation)
- Privacy-focused browsers (tracking may be blocked)

## Documentation Standards

### Code Comments

Add comments for form tracking:

```javascript
// GTM Form Tracking - Track form submission
if (
  window.GTMFormTracker &&
  typeof window.GTMFormTracker.trackCustomHTMLForm === "function"
) {
  window.GTMFormTracker.trackCustomHTMLForm(form, {
    formName: "Form Name",
    hubspotFormGuid: "guid-here",
  });
}
```

### Update Documentation

When adding/modifying forms:

- Update `form-audit-inventory.json`
- Update `form-ids-reference.md`
- Update `form-audit-report.md` (if significant change)

## Security Considerations

### No Sensitive Data

**Never include sensitive data in dataLayer:**

```javascript
// ❌ BAD - Sensitive data
{
  email: userEmail, // Don't include PII
  phone: userPhone, // Don't include PII
  password: userPassword // NEVER include passwords
}

// ✅ GOOD - Only tracking data
{
  form_id: 'demo-form',
  form_name: 'Main Lead Capture',
  form_type: 'custom_html'
}
```

### Validate Input

The tracking utility validates and sanitizes data automatically. Don't pass untrusted user input directly.

## Maintenance Best Practices

### Regular Audits

- **Quarterly:** Full form inventory audit
- **Monthly:** Check for new forms (run audit script)
- **After deployments:** Verify tracking still works

### Version Control

- Commit form inventory updates with descriptive messages
- Tag releases that include form tracking changes
- Document breaking changes in commit messages

### Monitoring

- Set up GA4 alerts for form submission drops
- Monitor dataLayer push errors (browser console)
- Review GTM Preview mode logs regularly

## Common Pitfalls to Avoid

### 1. Duplicate Tracking

**Problem:** Tracking fires multiple times for same form submission

**Solution:** Ensure tracking call happens only once, before form submission

### 2. Missing Form Context

**Problem:** Form name too generic, can't differentiate forms

**Solution:** Use descriptive form names with context (e.g., "Demo Form (Main)" vs "Demo Form (SDR)")

### 3. Incorrect HubSpot GUID

**Problem:** Wrong HubSpot form GUID in tracking

**Solution:** Always verify GUID from `hubspot-config.php` or HubSpot dashboard

### 4. Tracking After Submission

**Problem:** Tracking fires after form redirects/submits

**Solution:** Always track before `form.submit()` or fetch call

### 5. Inconsistent Event Structure

**Problem:** Different forms use different dataLayer structures

**Solution:** Always use `GTMFormTracker` utility - it ensures consistency

## Reference

- **Implementation Guide:** `docs/forms/GTM_FORM_TRACKING_GUIDE.md`
- **Developer Guide:** `docs/forms/FORM_TRACKING_DEVELOPER_GUIDE.md`
- **Troubleshooting:** `docs/forms/FORM_TRACKING_TROUBLESHOOTING.md`
