What's the right way to design a custom GA4 event?
Custom GA4 events should follow four design rules: (1) snake_case event name matching GA4's reserved-name convention (form_submit not formSubmit), (2) snake_case parameters in the dataLayer push, (3) parameters limited to the 25 custom dimension cap per property (don't send everything; choose what you'll actually report on), (4) reuse existing event names where possible (use select_content instead of inventing cta_click — Google's recommended events have built-in reporting).
Avoid reserved event names (page_view, purchase, view_item, etc.) for custom use. Below are eight recipes covering 80% of typical custom event needs.
The four design rules
Before any specific recipe, internalise the four rules:
Rule 1 — snake_case for everything. Event names and parameter names. Match GA4's convention.
Rule 2 — Don't reinvent recommended events. GA4 has 30+ recommended event names with built-in reports (login, sign_up, search, share, generate_lead, select_content, view_promotion, select_promotion, etc.). Use those before inventing custom names.
Rule 3 — Cap parameters at what you'll report on. GA4 has a hard limit of 50 event parameters per event and 25 custom dimensions per property (50 on 360). Don't send 30 parameters per event "just in case" — you'll hit the cap and start losing dimensions.
Rule 4 — Avoid reserved names. Don't fire page_view, purchase, view_item, add_to_cart, begin_checkout etc. unless you mean the standard semantics. Reserved names trigger built-in reports and fight with your custom logic.
Recipe 1 — Form submission tracking
The most common custom event. Fires when a user submits a non-checkout form (contact, demo request, newsletter signup).
dataLayer push:
GTM trigger: Custom Event - Event name = generate_lead
Why this design: Uses GA4's recommended generate_lead event (built-in reporting). form_id for technical reference, form_name for stakeholder readability. value enables Smart Bidding optimisation when used with offline conversion import.
Common mistakes: Using formSubmit (camelCase, won't match GTM triggers consistently). Sending the whole form data including PII (TOS violation). Missing value field that would unlock value-based bidding.
Recipe 2 — Scroll-depth milestones
Tracks how deep users scroll on long-form content. Useful for content engagement reporting and CMS optimisation.
dataLayer push:
GTM trigger: Built-in Scroll Depth trigger - thresholds at 25%, 50%, 75%, 90%
Why this design: Uses GA4's recommended scroll event. percent_scrolled is a numeric parameter (number, not string) for easy aggregation. page_section allows segmenting by which page area generated the scroll.
Common mistakes: Firing scroll event constantly during scrolling instead of at thresholds (creates massive event volume). Sending percent as string ('75') instead of number (75) — GA4 won't aggregate strings.
Recipe 3 — Video engagement
Tracks video play, pause, completion, and milestone progress.
dataLayer push:
For video start: event: 'video_start'. For completion: event: 'video_complete'.
GTM trigger: Built-in YouTube Video trigger handles YouTube videos automatically. For self-hosted video, custom HTML triggers on the <video> element's events.
Why this design: Uses GA4's recommended video events with consistent parameter naming (matches GA4's automatic video tracking via Enhanced Measurement). video_provider distinguishes YouTube/Vimeo/Wistia/self-hosted in reports.
Common mistakes: Sending video_id instead of video_title (less stakeholder-readable). Firing video_progress every second (event volume bomb) instead of at milestones (10%, 25%, 50%, 75%).
Recipe 4 — File downloads
Tracks PDF, document, or asset downloads.
dataLayer push:
Want to see whether attribution loss is already distorting your channel data?
GTM trigger: Click - All Elements with regex match on click URL for known file extensions (pdf, docx, xlsx, zip, mp4, etc.)
Why this design: Uses GA4's recommended file_download event (Enhanced Measurement automatic tracking covers basic case; custom extends it). file_extension enables filtering by document type.
Common mistakes: Tracking only PDF when business cases also need spreadsheet/asset downloads. Missing link_text — important for stakeholders trying to identify which CTA generated the download.
Recipe 5 — Outbound link clicks
Tracks clicks to external domains.
dataLayer push:
GTM trigger: Click - Just Links with hostname-not-equals condition (excludes internal links)
Why this design: Uses GA4's recommended click event with outbound: true parameter (matches Enhanced Measurement's automatic outbound click tracking format). link_domain enables top-domains-clicked reporting.
Common mistakes: Custom event name like outbound_link_click instead of standard click with outbound: true — fights with Enhanced Measurement.
Recipe 6 — Search behaviour
Tracks site search queries (separate from organic search).
dataLayer push:
GTM trigger: Custom Event - Event name = search. Fire from the search results page after results load.
Why this design: Uses GA4's recommended search event. search_results_count (number) lets you identify zero-result queries. search_filter_* captures whether refinements were applied.
Common mistakes: Firing search event before results load (results_count is wrong). Capturing search_term with PII in queries (e.g., users typing email addresses) — strip before push.
Recipe 7 — In-page CTA tracking
Tracks clicks on important page CTAs (hero buttons, pricing toggles, signup buttons).
dataLayer push:
GTM trigger: Click - All Elements with regex on click element class or ID matching your CTA naming convention
Why this design: Uses GA4's recommended select_content event (built-in content engagement reports). content_id for technical reference, content_name for stakeholder readability.
Common mistakes: Inventing custom event name like cta_click when select_content covers it. Tracking every clickable element on the page (event volume bomb) instead of just business-critical CTAs.
Recipe 8 — Rage-click detection
Identifies users who click multiple times rapidly on the same element (UX frustration signal).
dataLayer push:
GTM trigger: Custom Event - Event name = rage_click. Fire from custom JavaScript that detects 3+ clicks on the same element within 2 seconds.
Why this design: No GA4 recommended event for this UX signal — custom name is justified. click_count (number) lets you segment by frustration severity. Useful for identifying broken UI.
Common mistakes: Detection logic too sensitive (every double-click counts as rage). Not de-duplicating across sessions (one frustrated user fires 50 events).
What about reserved events?
Don't fire any of these as custom events with non-standard semantics:
page_view,first_visit,session_start— automatic events GA4 managespurchase,refund,add_to_cart,begin_checkout— e-commerce eventsview_item,view_item_list,view_promotion— e-commerce eventslogin,sign_up,share— recommended user events with built-in reportsselect_content,select_item,select_promotion— content/promotion eventssearch,view_search_results— search events
Firing reserved events with the wrong data shape (e.g., a purchase event without transaction_id) breaks GA4's reports. Worse, you can't easily fix it because GA4's data is append-only.
FAQ: Custom Event Design Patterns: 8 Recipes for Common Use Cases
What should a team validate first when custom event design patterns: 8 recipes for common use cases appears?
How do I know whether the fix actually worked?
When should this become a full GA4 audit instead of a quick fix?
Related guides for Custom Event Design Patterns: 8 Recipes for Common Use Cases
ChatGPT, Atlas, Perplexity, Comet, Claude: How Each Shows Up in GA4 (2026 Reference)
In 2026, AI traffic in GA4 splits into three buckets. Browsers and assistants that pass clean referrers (Perplexity web, Perplexity Comet, Claude.ai, Copilot, Gemini standalone) appear with a recognisable source / medium like perplexity.ai / referral. Surfaces that strip the referrer (ChatGPT Atlas…
Perplexity Sources Report: How to Influence What It Cites in 2026
Perplexity citations correlate strongly with five factors: (1) ranking in Bing's top 10 for the underlying query (Perplexity uses Bing's index as fallback alongside its own ~5 billion-URL custom crawler), (2) a clear direct answer in the first 50 words of the relevant page…
Check Custom Event Design Patterns: 8 Recipes for Common Use Cases before campaign reporting gets blamed for the wrong issue
Run a free GA4 audit to spot attribution breaks, UTM governance issues, self-referrals, and source/medium loss fast.