Should I use snake_case or camelCase in GA4?
Use snake_case for all GA4 event parameters and custom dimension names. GA4's standard event parameters use snake_case (page_location, transaction_id, engagement_time_msec) and the GA4 Data API expects snake_case. The dataLayer can be camelCase if your engineering convention demands it, but you'll need GTM Variables to translate field names before they reach GA4.
Mixed conventions in the dataLayer create the most common naming-related bugs — userID and user_id produce two separate dimensions that won't merge. Parameter names are limited to 40 characters and must start with a letter. Cases sensitivity: Membership_Tier and membership_tier are different parameters in GA4.
Why snake_case wins
Three concrete reasons GA4 standardised on snake_case:
1. GA4's standard parameters use it. page_location, page_title, page_referrer, ga_session_id, engagement_time_msec, transaction_id, purchase_revenue, item_id, currency — every Google-defined parameter is snake_case. Custom parameters that don't match this convention create visual inconsistency and force GTM Variables to handle two cases.
2. Data API expects it. When querying GA4 via the Data API, dimension names are referenced in snake_case format. Using camelCase in your custom dimensions forces translation in every API client.
3. BigQuery exports use it. The event_params array stores parameter keys as snake_case strings. Querying these in SQL is cleaner with consistent casing.
The exception: if you're forced to follow an engineering convention requiring camelCase (some style guides do), keep camelCase in the dataLayer but translate to snake_case before sending to GA4 via GTM Variables.
The 40-character limit
GA4 enforces a hard 40-character limit on parameter names:
super_long_descriptive_field_name_with_underscores— 50 chars, rejectedsubscription_plan_renewal_frequency— 35 chars, accepted
The discipline: keep names under 30 characters. Reserve some headroom for descriptiveness without hitting the cap.
Common over-long names we see:
customer_lifetime_value_calculated— abbreviate tocustomer_ltvoriginal_referring_session_source— abbreviate tooriginal_session_sourcefeature_flag_experiment_variant_id— abbreviate toexperiment_variant_id
Reserved parameter names
GA4 reserves specific parameter names with special semantics. Don't use these for custom purposes:
| Reserved name | Used for |
|---|---|
page_location | Full URL of the page |
page_title | Page title |
page_referrer | Referring URL |
page_path | Path component of URL |
screen_name | App screen name (mobile) |
engagement_time_msec | Engagement time on event |
session_id (and ga_session_id) | Session identifier |
client_id | Anonymous client ID |
user_id | Logged-in user ID |
transaction_id | Purchase identifier |
value, currency, tax, shipping | E-commerce monetary fields |
coupon, payment_type | E-commerce metadata |
items | E-commerce items array |
firebase_* | Firebase reserved namespace |
ga_* | Google Analytics reserved namespace |
Custom names matching these are silently overwritten by GA4's standard handling. Pick a different name if your custom field is something different from the standard meaning.
Case sensitivity matters
GA4 treats case differences as different parameters:
Want to see which hidden implementation gaps are affecting your GA4 data quality?
If your dataLayer has any of these inconsistencies, your reports show three columns for the same logical concept. Worse, conversion attribution can fragment across the variants.
The fix: enforce one casing convention in your dataLayer schema, and audit historical pushes for variants. If you find variants, deprecate them and standardise.
When dataLayer convention diverges from GA4 convention
Some engineering teams have strict camelCase or PascalCase conventions in their codebase. Forcing snake_case in the dataLayer creates friction.
The translation pattern in GTM:
- Engineering pushes camelCase to dataLayer:
``javascript window.dataLayer.push({ event: 'purchase', transactionID: 'T_12345', userID: 'U_98765', orderValue: 99.00 }); ``
- GTM Variables read the camelCase names:
{{DLV - transactionID}}{{DLV - userID}}{{DLV - orderValue}}
- The GA4 Tag maps Variables to snake_case parameter names:
- Parameter
transaction_id={{DLV - transactionID}} - Parameter
user_id={{DLV - userID}} - Parameter
value={{DLV - orderValue}}
GA4 receives snake_case while engineering keeps camelCase. The translation is centralised in GTM, so it's maintainable.
The cost: every time engineering adds a new field, GTM needs the corresponding Variable + Tag mapping. The benefit: engineering doesn't fight against their own conventions.
Custom dimension naming
When you register custom dimensions in GA4, the user-facing display name doesn't have to match the parameter name. Conventions:
- Parameter name (snake_case):
customer_ltv— what the dataLayer pushes - Display name (Title Case):
Customer LTV— what stakeholders see in reports
Keep these aligned conceptually. A parameter named customer_ltv should have a display name like Customer LTV or Customer Lifetime Value — not Plan Tier.
When you've shipped with mixed conventions
Audit pattern: query BigQuery for parameter name distinct values:
Look for case variants of the same logical concept. The result is your remediation list.
Migration: pick the canonical name (snake_case), add it to all future events, run dual (old + new) for 30 days, then deprecate old. Historical data stays under the old name; new data flows under the new name. Annotate the migration date.
FAQ: Parameter Naming Conventions: snake_case vs camelCase in GA4
How close should parameter naming conventions: snake_case vs camelcase in ga4 numbers be before I worry?
What should I validate first when parameter naming conventions: snake_case vs camelcase in ga4 numbers disagree?
When is a discrepancy a tracking bug instead of a reporting difference?
Related guides for Parameter Naming Conventions: snake_case vs camelCase in GA4
BigQuery Cost Optimisation for GA4 Exports: 9 SQL Patterns (2026)
The biggest cost wins come from nine SQL patterns: (1) partition pruning via _TABLE_SUFFIX BETWEEN (10–50x cost difference vs derived filters), (2) clustering on source/medium/event_name (30–60% reduction on top of partitioning), (3) explicit column selection (never SELECT *)…
How to Stitch GA4 BigQuery Sessions Manually (2026)
GA4 doesn't store sessions as records in BigQuery exports — only individual events with session identifiers. To reconstruct sessions: join on user_pseudo_id + (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id') as the unique session key…
Run a GA4 audit before parameter naming conventions: snake_case vs camelcase in ga4 spreads into reporting decisions
Use GA4 Audits to surface implementation gaps, broken signals, and the next fixes to prioritize before the issue becomes harder to trust or explain.