We've implemented GrowthBook through React and use...
# ask-questions
w
We've implemented GrowthBook through React and use GA4 as our data source. Do we need to have a tracking callback in this case and how do we implement this?
r
Hi Sjoerd, yes you will need to add a trackingCallback. An example of this can be found here<.> The only SDK where you don't have to add a trackingCallback will be the HTML Script SDK.
Are you also using GTM in your implementation?
w
We are using GTM, yes. Do we just need to push trackingCallback to the datalayer?
w
We use Next.JS for the implementation instead of GTM. I think the docs article is not relevant in this situation?
Is a datalayer push with event_name experiment_key and variation in this case enough?
r
It should suffice sending the following to the data layer and forwarding to your data source: { "event": "experiment­_viewed", "experiment­_id": "...", "variation­_id": "..." }
w
Okay, thank you. We will try that!
🙏 1
w
Hey Natasha, I am trying to implement the trackingCallback. Now we are pushing it to the datalayer in GTM, but I am not receiving anything there. I am also not seeing the console log which I implemented. For feature flagging everything works correctly. I don't know if someone from GrowthBook can look into our account and help us further to get experiments working or do you have any other idea's that might cause this?
Copy code
const growthbook: GrowthBook = new GrowthBook({
    apiHost: process.env.NEXT_PUBLIC_GROWTHBOOK_API_HOST || '',
    clientKey: process.env.NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY || '',
    // Enable easier debugging during development
    enableDevMode: process.env.NEXT_PUBLIC_GROWTHBOOK_DEV_MODE === 'true',
    // Update the instance in realtime as features change in GrowthBook
    subscribeToChanges: true,
    trackingCallback: (experiment, result): void => {
        // eslint-disable-next-line no-console
        console.log('Experiment Viewed', {
            experimentId: experiment.key,
            variationId: result.key,
        })

        window.dataLayer = window.dataLayer || []
        window.dataLayer.push({
            event: EventName.EXPERIMENT_VIEWED,
            experiment_id: experiment.key,
            variation_id: result.key,
        })
    },
})
r
Hi Mike, apologies for the delayed response here. The console.log might not be visible when the script is injected via GTM due to the sandboxed JavaScript environment that GTM uses.
Just to confirm are you still not seeing the experiment­_viewed event in GA4?
w
Hey Natasha, we got everything working eventually, had to with the assignment variable not matching
r
Ah perfect, thank you for the update Mike and super glad you were able to get this resolved! Happy testing 🚀
w
We currently have the following message: Multiple Exposures Warning. 1.233 users (6,86%) saw multiple variations and were automatically removed from results. Check for bugs in your implementation, event tracking, or data pipeline.
Any idea what might cause this Natasha?
r
Hi Mike, there's usually 2 possible culprits for this and they are in our troubleshooting guide here- https://docs.growthbook.io/kb/experiments/troubleshooting-experiments#problem-3-multiple-exposures
Please, can you review the above and confirm of any of them match your usecase?
w
I imagine it is a mismatch in id's, currently we are experimenting on anonymous_id which is a uuid created in the frontend. However the assignment query is as following
Copy code
SELECT
2  user_pseudo_id as anonymous_id,
Could this be the issue?
r
Whats the identifier used in your data source?
w
image.png
r
That looks correct to me, can you review the identifier in your data warehouse?
w
anonymous_id that is from the frontend needs to match the user_psuedo_id in ga4/bigquery right?
gtag creates that id but i also create and use a uuid in a long lived cookie that i sent as the anonymous_id
Do I not need to get the user_psuedo_id somehow from gtag or is my thinking incorrect?
r
Yes you're correct, the ​`anonymous­_id`​ that you're using in the frontend should match the ​`user­_pseudo­_id`​ in GA4/BigQuery. This is important for ensuring that your experiment data is correctly associated with the right users.
We had another customer in the past who had a similar issue...... So what can happen with your implementation, on first load, there is no GA4 cookie, so you'll create a GrowthBook uuid (​`anonymous­_id)`​, then on load N+1, the cookie will be there, and they'll get the ga­_client id (​`user­_pseudo­_id)`​
if that first load is super important, we suggest you always use the gb one, as you can be sure it will always exist. What attribute you use for assignment, need not be the id used for analysis
When the track event happens, even if you have the gb­_uuid, it will have the ga4 client id in the data. So you can pass back the gb­_uuid, or just rely on the GB­_uuid having a one to one mapping to their ga4 client­_id, but switching the id can cause the multiple exposure warning
w
Does that happen in the background ?
We do not touch GA4 in the growthbook implementation
r
Ah sorry I thought you used React, GA4 and GTM
w
We do however
This is our set attribute implementation:
void growthbook.setAttributes({
anonymous_id: anonymous_id,
deviceId: uuid,
userAgent: navigator.userAgent,
deviceType: /Mobi|Android|iPhone/i.test(navigator.userAgent)
? 'mobile'
: 'desktop',
browser: getCurrentBrowser(),
store: getStoreByLocale(router.locale ?? 'nl'),
hasOfficeIp: hasOfficeIpValue,
loggedIn: false,
})
anonymous_id is a uuid that is generated
r
Let me check this with the team and get back to you
w
Sounds good, if anonymous_id needs to be the same as user_psuedo_id that is used at google I would like to know how to get that user_psuedo_id in the frontend
r
Thank you from what I have seen thats no easy feat. Will ask the team for the best and more simple solution here and get back to you shortly.
w
Do you have an update about this Natasha ?
r
Hi Mike, unfortunately, I am still awaiting a response on this. However, I did come across a past case where the customer was in a similar situation and was recommended to use gtag. Unsure if this is applicable to your business case, but worth sending it over whilst we await the teams response: We have had some issues with using data layer instead of gtag- we sometimes saw that it would add to the datalayer but not actually pass the event to ga4 until another page load Could you try using gtag? The sample code would look like this: trackingCallback: (experiment, result) => { // track using GA4 window.gtag("event", "experiment­_viewed", { event­_category: "experiment", experiment­_id: experiment.key, variation­_id: result.variationId, }) } To cover all bases you could implement the following: if (window.gtag) { window.gtag("event", "experiment­_viewed", { {add your ID here}: {add back here your id}, event­_category: "experiment", experiment­_id: experiment.key, variation­_id: result.variationId, }); } // GTM - dataLayer if (window.dataLayer) { window.dataLayer.push({ event: "experiment­_viewed", {add your ID here}: {add back here}, event­_category: "experiment", experiment­_id: experiment.key, variation­_id: result.variationId, }); }
Could using gtag be a solution for your business case?
w
We do not have gtag initialized unfortunately
r
Ah I see, I'll send a nudge to the team and let you know once they have reviewed your exact business case to see whats the best course of action. Thank you for your ongoing patience and apologies for any inconvenience caused
1
w
Would setting the ID in the tracking callback here also be a solution perhaps?
if (typeof window !== 'undefined') {
window.dataLayer = window.dataLayer || []
window.dataLayer.push({
event: EventName.EXPERIMENT_VIEWED,
experiment_id: experiment.key,
variation_id: result.key,
})
}
r
It was a solution for the other customer. It maybe worth a shot trying whilst we await the team response, as it will be tomorrow in the PST timezone that they come online
w
@flaky-noon-11399 hi Natasha, do you maybe have an update on this for us?
f
Hi Sjoerd, unfortunately, I'm still awaiting a response from the team on this. I'll escalate this internally to Management so we can get a response. Sorry again for any inconvenience caused and thank you for your ongoing patience
🙏 1
w
Still nothing Natasha?
f
Sorry Mike, this has been escalated to Management to take over so I'm not getting alerted on responses here. I will chase this up internally with management.
w
Okay, hope to hear something soon as it is taking quite some time
w
@flaky-noon-11399 do you have an update on this?
@flaky-noon-11399 can you or anyone else help us with this? We're still running into this problem.
f
Oh super sorry, the Support team are no longer monitoring the Community, as the Community team maintain this source, so I'm not notified on messages and just checked the community on a whim and saw this message 🙈. Apologies for that. Let me re-escalate this with Support Management and the Community team. I'm struggling to find the original ticket that was piped into our old Support tool that had an escalation note with your issue, so please correct me if i'm wrong here........
The issue is that you are seeing multiple exposure warnings in your GA/BQ/React SDK implementation.
Please can you send a screenshot of the experiment overview tab, results tab and experiment health tab so we can review?
Your assignment attribute is anonymous_id (is a uuid you are setting in the cookie) and your identifier in your data source is user_psuedo_id correct?
Do you have an identifier join between both these idenitfers under your datasource?
w
screencapture-app-growthbook-io-experiment-exp-19g61wm3x7lit1-2024-12-18-09_00_46.png,screencapture-app-growthbook-io-experiment-exp-19g61wm3x7lit1-2024-12-18-09_00_40.png,screencapture-app-growthbook-io-experiment-exp-19g61wm3x7lit1-2024-12-18-09_00_29.png
screencapture-app-growthbook-io-attributes-2024-12-18-09_02_21.png,screencapture-app-growthbook-io-datasources-ds-19g61wm1rok5i5-2024-12-18-09_01_59.png
I screenshotted everything that could be of use haha. I don't think we use an identifier join
w
We are also implementing this solution, will that resolve it?
f
Thank you both, I have a meeting with Support Management later today on next steps here. Will keep you posted 🙏
🙏 1
b
Hi all, August here from the GrowthBook Support team. The issue does seem to be a mismatch between the anonymous_id and the user_pseudo_id. Multiple Exposure Warnings are a common result of this and you can read more about it here if you haven't already: https://docs.growthbook.io/kb/experiments/troubleshooting-experiments#solution-3-ensure-the-hash-attribute-and-identifier-type-match Nicolaus' suggestion in the screenshot you sent should work.
To handle using both
anonymous_id
and
user_pseudo_id
while using GA4 with GrowthBook, in general you need to pass both of those attributes to the trackingCallback. 1. Set both identifiers as attributes when initializing GrowthBook:
Copy code
javascript
growthbook.setAttributes({
  id: user.id, // or generateAnonymousId() for logged-out users
  anonymousId: getAnonymousId() // function to get GA4 client_id or user_pseudo_id
});
2. In your GA4 BigQuery setup, use an assignment query that includes both identifiers:
Copy code
sql
SELECT
  user_pseudo_id as anonymous_id,
  user_id as id
FROM ...
3. When creating an experiment, choose which identifier to use for traffic splitting based on your needs. Use
anonymous_id
for experiments that include both logged-in and logged-out users, and
id
for experiments only targeting logged-in users. 4. Ensure you're passing both identifiers in your tracking callback to GA4. (As I mentioned just above.) 5. Set up identifier join tables in your GrowthBook data source to merge the identities during analysis. (This is what Natasha was asking about earlier.) You might still see some multiple exposure warnings, especially if users transition from anonymous to a logged-in state.
w
Thanks for the help! We don't collect any user id's in GA4, so I suppose it would suffice to only collect user_pseudo_id, right? If we name everything user_pseudo_id, that would also make everything more simple, I guess? Instead of using anonymous_id and user_pseudo_id
b
You're welcome!
I suppose it would suffice to only collect user_pseudo_id, right?
You can use anything you want as the ID for assignment / hashing, as long as it is unique to each user.
If we name everything user_pseudo_id, that would also make everything more simple, I guess?Instead of using anonymous_id and user_pseudo_id
Yes.
🙏 1
100 Views