Custom dimensions: scope, registration and the retroactivity trap
Sending the parameter and being able to report on it are two different jobs, usually done by two different people.
A GA4 custom dimension makes an event parameter or user property reportable. Registration is a separate act from collection, it is not retroactive, and data collected before the dimension existed never appears against it. The three scopes, event, user and item, each have their own quota and their own correct use, and choosing the wrong one produces a dimension that is technically populated and analytically useless.
Collection and reporting are two systems
GA4 accepts almost any parameter you send. It does not follow that you can report on it. The parameter arrives, sits on the event, flows into the BigQuery export and into audiences, and does not appear as a dimension anywhere in the interface until somebody registers it.
That gap is the single most common reason for the conversation that starts with the developer saying the parameter is definitely being sent and the analyst saying it is definitely not in the report. Both are right.
Google's documentation on creating event-scoped custom dimensions is explicit about the sequence and the delay: data appears in reports 24 to 48 hours after both the data has been sent and the custom dimension has been created. Both conditions, not either.
So the handover is the risk. Sending is a code change; registering is an Admin change; and the two are usually owned by different people on different schedules. Any process that does not name both owners on the same line will eventually drop one.
The trap: registration does not look backwards
The expensive part is what happens when you register late. The dimension starts populating from the moment it exists and does not backfill. Every event collected before that moment carries the parameter in the raw data and reports nothing against the new dimension.
In the interface that period shows as not set, or simply as an absence. It reads like a collection failure, which sends people to check the tag, which is working. The tag was always working.
For a business that took nine months to notice, that is nine months of history that cannot be sliced by service line, or location, or plan tier. GA4 will not repair it, and no amount of correct tagging afterwards changes what the aggregation already stored.
There is one genuine escape, and it is worth knowing before you need it. If the property was linked to BigQuery, the parameter is in the raw export for the whole period, because the export writes every parameter regardless of registration. The history is recoverable in SQL even when it is unrecoverable in the interface, which is one more argument for linking BigQuery on day one.
The three scopes
| Scope | Sourced from | Standard limit | Use it for |
|---|---|---|---|
| Event | An event parameter | 50 | Something true of one action: service line, form variant, search term |
| User | A user property | 25 | Something true of the person across sessions: plan tier, account type, membership status |
| Item | A parameter inside the ecommerce items array | 10 | Something true of a product line: colour, size, supplier |
The limits come from Google's custom dimensions and metrics page, which also lists 50 custom metrics and 5 calculated metrics on a standard property.
Scope is not a preference, it is a statement about what the value describes. A plan tier registered as event scoped answers the question what tier was this event, which is nearly the same answer and behaves differently the moment you segment users rather than events.
User scope has a mechanical prerequisite: the value has to be sent as a user property rather than as an event parameter. Sending user properties is a distinct call, and it carries its own tighter limits, 24 characters for a name and 36 for a value, per the collection limits.
Item scope only exists inside the ecommerce items array, and only ten slots exist. That constraint shapes the ecommerce design more than anything else, which is why it is treated as a first-class decision in the GA4 ecommerce schema.
What not to register
Google publishes a short list of things that should never become custom dimensions, and every item on it comes from a real failure. Do not create a custom dimension for a unique identifier per user; use the User-ID feature instead. Do not send a session identifier. Do not send a timestamp.
The reason is cardinality. Each of those produces a dimension with as many values as there are users, sessions or milliseconds, which drives tables past their row limits and condenses the results into an (other) row. The mechanics are set out in the GA4 limits you will hit.
Google also warns against registering a parameter that is already a predefined dimension, such as page path or transaction identifier. It does not hurt cardinality, it simply spends a slot to duplicate something you already had.
And there is a data-type trap with real consequences. Google states that any custom dimension value resembling a number is treated as a number, giving 9343.324234 becoming 9343.32 as the example. Anything with leading zeros, such as a Canadian postal code fragment or a padded store number, loses them.
The quota is a design constraint, not paperwork
Fifty event-scoped slots sounds generous until a multi-brand, multi-location business starts allocating them, and then it is a real design problem. Two habits get you back most of the space.
The first is consolidation. One dimension called service_line used across ten events beats ten event-specific dimensions, and Google's own guidance now points that way, noting that the older per-event registration model is deprecated and advising the removal of duplicate registrations across events.
The second is deliberate non-registration. Google explicitly suggests sending high-cardinality data through parameters and user properties without registering it, so that it stays usable in BigQuery, audiences and segments without consuming property limits. That is the right home for order numbers and product identifiers.
Be aware of the swap cost before you plan around it. Google notes a 48-hour wait after deleting dimensions before new ones can be added, so a same-day reshuffle is not available.
The process that stops this recurring
Make registration part of the definition of done for any tracking change, not a follow-up task. The tracking plan row is not complete when the event fires; it is complete when the parameter is registered, the dimension name is agreed, and somebody has seen a value in a report.
Write the dimension name down at design time, in the same document as the parameter name, because the two are separate strings and the mismatch between them is a recurring source of confusion. That is the same argument as acceptance criteria for analytics work.
Then confirm rather than assume. Wait the documented 24 to 48 hours, open a report, and check that the values look like the vocabulary you designed rather than like a free-text field somebody typed into. Values arriving as a mix of Ottawa, ottawa and OTTAWA is a naming failure that registration will happily preserve forever.
For a multi-location business this is the whole ball game. Correcting the event layer so that location resolved on every conversion is what made per-location reporting possible across more than fifteen clinics, and registration was the step that turned a collected parameter into a number the operator could act on.
If the registry has already drifted past anyone's understanding, treat it as an audit rather than a cleanup. Scoping GA4 and Tag Manager work around a reviewed custom definition list is cheaper than discovering the gap a quarter into a reporting project.