Data Types
The wrong data type silently corrupts your analytics. GA4 receives price: "49.99" (a string) and cannot sum it into revenue totals. It receives quantity: "2" and cannot compute item-level metrics. These aren’t validation errors that get flagged — they’re silent failures that make your ecommerce reports wrong without any obvious warning sign.
This page defines the correct data types for every common dataLayer parameter.
Strings
Section titled “Strings”Use strings for:
- Names, labels, and descriptive text
- Category paths
- Identifiers (IDs, SKUs, order numbers) — even if they look like numbers
- Categorical flags with fixed values (
'credit_card','standard','gold') - Currency codes
- ISO date strings
// ✅ Correct string usageitem_id: 'SKU-001'item_name: 'Classic Leather Jacket'item_category: 'Apparel'item_variant: 'Black / Large'currency: 'USD'transaction_id: 'ORD-2024-98765'payment_type: 'credit_card'shipping_tier: 'express'event_date: '2024-03-15'
// ❌ Numbers that should be strings (they're identifiers, not quantities)item_id: 12345 // Will be fine in dataLayer, but inconsistentuser_id: 98765 // Use '98765' — user IDs are labels, not amountsorder_number: 1001 // Use '1001'String length limits
Section titled “String length limits”GA4 truncates string parameter values at 100 characters. User property values are truncated at 36 characters. Design your string values to be categorical and short — not full sentences or descriptions.
Currency codes
Section titled “Currency codes”Currency must always be a three-letter ISO 4217 code in uppercase.
// ✅ Correctcurrency: 'USD'currency: 'EUR'currency: 'GBP'currency: 'SEK'
// ❌ Wrongcurrency: 'usd' // lowercasecurrency: '$' // symbolcurrency: 'US Dollar' // full namecurrency: 'Dollar' // partial nameNumbers
Section titled “Numbers”Use numbers for:
- Prices and monetary values
- Quantities
- Scores and ratings
- Percentages and ratios
- Durations in seconds
- Counts
// ✅ Correct number usageprice: 49.99quantity: 2value: 99.98tax: 9.99shipping: 5.00discount: 7.50rating: 4.5reading_time_seconds: 240scroll_depth_percent: 75The critical price mistake
Section titled “The critical price mistake”Prices as strings are the most common dataLayer error. GA4 silently ignores string values where it expects a number, which means your revenue metrics will be zero or incorrect.
// ❌ All of these are wrong for price/value fieldsprice: '49.99' // String — GA4 ignores itprice: '$49.99' // String with currency symbolprice: '49,99' // European decimal format (comma)price: '1,234.56' // Comma-formatted numberprice: 49.9900 // Fine technically, but trailing zeros are noiseprice: null // Null — should be 0 or omitted
// ✅ Correctprice: 49.99value: 1234.56tax: 0.00If your backend returns prices as strings (e.g., from a JSON API that formats currency), convert them before pushing:
// Convert string price to numberprice: parseFloat(product.price),
// Or using Number()value: Number(order.total),Prices are unit prices, not line totals
Section titled “Prices are unit prices, not line totals”For the items array, price is the unit price per item — not the total for that line. GA4 calculates the line total as price × quantity.
// ✅ Correct — unit price{ item_id: 'SKU-001', item_name: 'T-Shirt', price: 24.99, // Unit price quantity: 3 // GA4 calculates line total as 74.97}
// ❌ Wrong — passing line total as price{ item_id: 'SKU-001', item_name: 'T-Shirt', price: 74.97, // Line total — GA4 will calculate 224.91 for quantity: 3 quantity: 3}Rounding
Section titled “Rounding”Round monetary values to two decimal places before pushing. JavaScript floating-point arithmetic can produce values like 0.30000000000000004 when you calculate 0.1 + 0.2. Use a rounding helper:
function roundPrice(value) { return Math.round(value * 100) / 100;}
// Orprice: parseFloat(product.price.toFixed(2))Booleans
Section titled “Booleans”Use booleans for binary flags — things that are either true or false, on or off.
// ✅ Correct boolean usageis_logged_in: trueis_sale_item: falsehas_active_subscription: trueis_new_customer: falsehas_applied_coupon: trueBooleans vs. boolean-as-string
Section titled “Booleans vs. boolean-as-string”GA4 can receive boolean values, but they’re often treated as strings in reports. For filtering in GA4’s Explore UI, boolean parameters show up as true or false strings. That’s fine — the important thing is consistency. Don’t mix true (boolean) and 'true' (string) for the same parameter across different events.
// ❌ Inconsistent — sometimes boolean, sometimes stringis_logged_in: true // on login eventis_logged_in: 'true' // on purchase event (string this time)is_logged_in: 1 // on some other event (number this time)
// ✅ Consistentis_logged_in: true // always a booleanArrays
Section titled “Arrays”Use arrays for collections — the most important being the items array in ecommerce events.
// ✅ Array of item objectsitems: [ { item_id: 'SKU-001', item_name: 'Classic Leather Jacket', price: 89.99, quantity: 1 }, { item_id: 'SKU-047', item_name: 'Cotton Crew T-Shirt', price: 24.99, quantity: 2 }]GTM and arrays
Section titled “GTM and arrays”When GTM processes a dataLayer push, arrays are handled differently from objects. Objects are recursively merged into GTM’s internal data model. Arrays replace the previous array value entirely — they are not merged.
This is critical for ecommerce: always clear ecommerce data (dataLayer.push({ ecommerce: null })) before pushing a new ecommerce event. Otherwise the items array from a previous event can persist in GTM’s model.
Objects
Section titled “Objects”Use objects for nested data structures — primarily the ecommerce object in GA4 ecommerce events.
// ✅ Nested object structuredataLayer.push({ event: 'purchase', ecommerce: { transaction_id: 'ORD-2024-98765', value: 142.97, currency: 'USD', items: [...] }});Keep nesting to two levels where possible. Deeply nested objects (three or more levels) become hard to reference in GTM Data Layer Variables using dot notation, and the paths become error-prone.
// ✅ Two levels — easy to reference as ecommerce.transaction_iddataLayer.push({ ecommerce: { transaction_id: '...' }});
// ❌ Three levels — now you need ecommerce.order.details.transaction_iddataLayer.push({ ecommerce: { order: { details: { transaction_id: '...' } } }});Null and undefined
Section titled “Null and undefined”Omit parameters you don’t have values for rather than passing null or undefined. An absent parameter is treated as “not provided” in GA4. A null value is treated as the value null, which may interfere with aggregation.
The exception is the ecommerce clearing pattern: dataLayer.push({ ecommerce: null }). This is a deliberate instruction to GTM to clear ecommerce from its internal data model. It’s not a data value — it’s a lifecycle management step.
// ✅ Omit parameters you don't have{ item_id: 'SKU-001', item_name: 'Classic Jacket', price: 89.99, quantity: 1 // No item_brand — just omit it}
// ❌ Passing null for missing parameters{ item_id: 'SKU-001', item_name: 'Classic Jacket', item_brand: null, // Don't do this price: 89.99, quantity: 1}
// ✅ Exception — the ecommerce clear (this is intentional)dataLayer.push({ ecommerce: null });Date and time values
Section titled “Date and time values”When you need to pass timestamps or dates, use ISO 8601 format strings.
// ✅ ISO 8601 date stringevent_date: '2024-03-15'order_created_at: '2024-03-15T14:30:00Z'subscription_expires: '2025-03-15'
// ❌ Ambiguous formatsevent_date: '03/15/2024' // US format — unclear internationallyevent_date: '15.03.2024' // European formatevent_date: 1710512400000 // Unix timestamp — use string insteadFor analytics purposes, date strings are almost always better than Unix timestamps. They’re readable in reports, filterable in GA4 Explorations, and don’t require conversion.