The GA4 custom dimensions worth registering for an app
Updated 31 October 2026.
A standard GA4 property allows 50 event-scoped custom dimensions, 25 user-scoped and 10 item-scoped. We used eight on one mobile app. Each one exists to answer a question somebody had already asked in a meeting and nobody could answer from the reports.
Why a parameter you are already sending stays invisible
You can send a parameter on every event for a year and never see it in a report. GA4 does not expose event parameters until they are registered as custom dimensions, and that registration is a separate job from the code that sends them.
This is where app analytics projects stall. The developer ships test_type on test_start, the marketing team opens the Events report, sees test_start with a count and nothing else, and concludes the parameter was never built. It was built. It is in the data, unexposed.
Firebase is explicit about it. Custom parameters have to be registered under Custom Definitions before they appear in reports, and Google’s setup order is code first, one real session second, dimension third. We now write the dimension list into the spec on every mobile app build, so the registration happens the same week as the release.
The eight we registered and what each one answers
Eight dimensions, one question each. We wrote the questions first, in the words the client had used out loud, then worked backwards to the parameter that would answer each one.
None of them describe the person. Every one describes something about the event: which test, which step, which failure, which payment method. That is deliberate, and the reason is scope. This is the list as it went into the property on 8 October 2026, for an assessment app on Android and iOS.
| Dimension | Parameter | Scope | The question it answers |
|---|---|---|---|
| Test type | test_type | Event | Which categories of assessment get started, and which get finished |
| Test ID | test_id | Event | Which single assessment loses the most people part way through |
| User type | user_type | Event | Whether new and returning users behave differently on the same screen |
| Method | method | Event | Whether phone, Google or Apple sign-in fails more often |
| Step | step | Event | Where inside a multi-screen form or a long test people stop |
| Error reason | error_reason | Event | Whether a failure was an OTP timeout, a wrong code, a network drop or an account that already existed |
| Practitioner ID | practitioner_id | Event | Whose slots get booked, and whose get viewed and abandoned |
| Payment method | payment_method | Event | Which payment method fails, and on which platform |
Event scope, user scope and item scope
Three scopes, and picking the wrong one is the usual mistake. An event-scoped dimension describes a single event, such as the test_type on this particular test_start. A user-scoped dimension describes the person and attaches to every event they send afterwards. An item-scoped dimension describes a product inside an ecommerce items array.
The limits differ because the cost differs. A standard property allows 50 event-scoped custom dimensions, 25 user-scoped and 10 item-scoped. Analytics 360 allows 125, 100 and 25.
All eight of ours are event-scoped. user_type looks like a user property and we still send it as an event parameter, because what we want to know is how a new user behaved on a specific screen on a specific day, not what label the person carries forever. If the label genuinely has to follow the person across sessions, that is when user scope earns its place, and you have 25 slots for it.
The fifty you get and how fast you use them
Fifty event-scoped dimensions sounds like plenty. It is, for one app, as long as one person owns the list.
It stops being plenty when three teams register their own. Marketing adds campaign_name, utm_content and a second copy of source. Product adds feature_flag, ab_variant and experiment_id. Engineering adds api_version and build_number. Nobody removes anything, because removing a dimension breaks the report that used it.
Keep one sheet with the dimension name, the parameter, the events it rides on, the owner and the date it was created. Ours has eight rows and a date column, and the date column is the one people use most, because it tells you how far back any report on that dimension can go. That sheet is a deliverable on our analytics work, not a private note.

ElectreeFi
SEO evidence, not app analytics. ElectreeFi is an EV charging network whose organic sessions rose 39.5% across thirteen months of work with us. It is here because it is a result we can show with a source behind it. The custom dimensions in this post were registered for an assessment app on Android and iOS that we are not naming.
They fill with what arrives after you create them
Treat a custom dimension as starting on the day you create it. We register them on release day, with the code, rather than the week after somebody asks for a report, because the report can only draw on what has arrived while the dimension existed.
Then expect a wait. Google’s documentation says that after 24 to 48 hours you can start to report on and advertise with the custom dimension or metric. If you check at lunchtime on release day and see (not set) everywhere, nothing is broken yet.
(not set) is also what you see when the parameter genuinely did not arrive on that event. That ambiguity is why the device check in DebugView comes before the report check, every single time. DebugView shows the last 60 seconds of events with their parameters, so you can see the value on the event rather than guessing from an empty column.
The reports this makes possible
Four reports we could not build before and could build after. A funnel from sign_up_start to sign_up broken down by method, which showed which sign-in route was losing accounts. A free-form exploration of test_start against test_complete by test_id, which is how you find the one assessment that is too long.
Then a table of payment_failed by payment_method and platform, which is a product bug report rather than a marketing report, and a comparison of test_complete rates by user_type, which answered whether returning users finish more than first-timers.
None of these are advanced. They are the reports you would have assumed already existed. Business analytics work is mostly this: getting a property into a state where ordinary questions have ordinary answers.
Naming rules that save you a rebuild
Lower case with underscores, no spaces, and the parameter name identical in the Android code, the iOS code and the GA4 Admin screen. Firebase event and parameter names are case sensitive, so test_type and Test_Type are two different things, and you will lose an afternoon finding that out.
Name the dimension after what it holds, not where it came from. test_id, not assessment_identifier_new. Where a value can be either an ID or a readable label, send both: practitioner_id to join back to the database, and a readable test_type to group by in reports, because nobody reads a table of 400 identifiers. That pairing also keeps one low-cardinality column in every report. A dimension holding thousands of distinct values is unreadable on screen even when it is technically correct, which is why test_type travels alongside test_id on every event in the assessment chain.
What we would not register
Anything containing an answer or a score. On this app the assessment answers and the scores never reach analytics at all, so there is nothing to register. The event says test_complete with a test_type and a test_id, and that is the whole record.
Anything that identifies a person. user_id is an internal identifier, never an email address, a phone number or a name. Google’s User-ID documentation says the ID must not contain information a third party could use to determine a user’s identity, and caps it at 256 characters. A custom dimension holding an email address is the same mistake with extra steps.
And anything you cannot name a decision for. We were once asked to register device model as a custom dimension; GA4 already reports device model, and the request existed because nobody had looked. Check the standard reports first. Half the dimensions people ask for on an app we build are already sitting there.
References
- Google Analytics Help, GA4 custom dimensions and metrics, accessed 11 October 2026
- Firebase documentation, Log events, accessed 11 October 2026
- Google Analytics Help, GA4 User-ID, accessed 11 October 2026
- Google Analytics Help, GA4 DebugView, accessed 11 October 2026
- Google Analytics Help, GA4 data retention, accessed 11 October 2026
Frequently asked questions
How many custom dimensions does GA4 allow?
50 event-scoped, 25 user-scoped and 10 item-scoped on a standard property. Analytics 360 allows 125, 100 and 25.
Why does my custom dimension show not set?
Either the parameter did not arrive on that event, or you are looking too soon. Google says to allow 24 to 48 hours after sending the data and creating the dimension before you can report on it, so check the event in DebugView on a real device first and the report afterwards.
Will a custom dimension show data from before I created it?
Treat it as starting from the day you create it. Google’s own setup order is to send the parameter first, let a session arrive, then create the dimension and wait 24 to 48 hours, which is why we register dimensions on the day a release ships rather than weeks later.
Should user_type be a user property or an event parameter?
An event parameter, if the question is how a new user behaved on one screen on one day. Use a user-scoped dimension only when the label genuinely has to follow the person across every later event, and remember you get 25 of those against 50 event-scoped.
Can I delete a custom dimension to free up a slot?
You can, and it breaks every report built on it. Keep a sheet of what is registered, which events carry it and who owns it, and with 50 event-scoped slots on a standard property most single apps never come close to the limit.
What does setting this up cost?
Registering eight dimensions is a couple of hours of configuration. The time goes into the code that sends the parameters, and app development with us starts at ₹75,000 for a PWA, ₹1,49,999 for Standard and ₹2,29,999 for Advanced across iOS and Android, plus GST.

