little-balloon-64875
03/18/2025, 1:44 PMlittle-balloon-64875
03/18/2025, 1:45 PMlittle-balloon-64875
03/18/2025, 1:50 PMsteep-dog-1694
03/18/2025, 3:24 PMuser_id
, and then hoping to segment by anonymous_id
?little-balloon-64875
03/18/2025, 3:59 PMid
, and then "Experiment Assignment Table" is anonymous_id
steep-dog-1694
03/18/2025, 4:04 PMuser_id
have a unique anonymous_id
? Or can a single user_id
have multiple `anonymous_id`s?fresh-football-47124
little-balloon-64875
03/18/2025, 4:16 PMgbuuid
and the Segment anonymous_id
should be the same? One shouldn't be 123456
and the other asdfjkl
?fresh-football-47124
little-balloon-64875
03/18/2025, 4:29 PMlittle-balloon-64875
03/18/2025, 4:36 PMimport { Experiment, Result } from "@growthbook/growthbook"
import {
GrowthBookProvider as GBProvider,
GrowthBook,
} from "@growthbook/growthbook-react"
import { useRouter } from "next/router"
import { useEffect, useState } from "react"
import { getSegmentAnonymousIdWithRetry } from "src/utils/segment-utils"
import getUUID from "src/utils/user-uuid"
const onExperimentViewed = (
experiment: Experiment<any>,
result: Result<any>
) => {
const experimentId = experiment.key
const variationId = result.key
const userId = getUUID()
setTimeout(() => {
// Sometimes the window object is not available, like if
// Segment is not allowed to load because of GDPR or whatever
// In this case we add ?. to avoid attempting to fire this
// if the analytics object is not available
window?.analytics?.track("Experiment Viewed", {
experimentId,
variationId,
userId,
})
}, 2000)
}
// Create a client-side GrowthBook instance
const gb = new GrowthBook({
apiHost: process.env.NEXT_PUBLIC_GROWTHBOOK_API_HOST,
clientKey: process.env.NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY,
decryptionKey: process.env.NEXT_PUBLIC_GROWTHBOOK_DECRYPTION_KEY,
// Enable easier debugging of feature flags during development
enableDevMode: true,
trackingCallback: onExperimentViewed,
})
// Let the GrowthBook instance know when the URL changes so the active
// experiments can update accordingly
function updateGrowthBookURL() {
gb.setURL(window.location.href)
}
const GrowthBookProvider = ({ children }: { children: React.ReactNode }) => {
const router = useRouter()
useEffect(() => {
// Initialize GrowthBook and make sure the attributes are set properly
gb.init({ streaming: true })
// Initial setup with UUID
const uuid = getUUID()
gb.setAttributes({ id: uuid, anonymousId: uuid })
// Try to get Segment's anonymousId and set it if available
getSegmentAnonymousIdWithRetry().then((anonymousId) => {
if (anonymousId) {
gb.setAttributes({ id: uuid, anonymousId: anonymousId })
}
})
router.events.on("routeChangeComplete", updateGrowthBookURL)
return () => router.events.off("routeChangeComplete", updateGrowthBookURL)
}, [])
return <GBProvider growthbook={gb}>{children}</GBProvider>
}
export default GrowthBookProvider
I'm generating and storing a gbuuid
cookie like so:
import Cookies from "js-cookie"
const getUUID = (): string => {
const COOKIE_NAME = "gbuuid"
const COOKIE_DAYS = 400 // 400 days is the max cookie duration for Chrome
// Only run on client side
if (typeof window === "undefined") return ""
// Generate a UUID using the most appropriate method available
const genUUID = (): string => {
if (window?.crypto?.randomUUID) return window.crypto.randomUUID()
return `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, (c) =>
(
Number(c) ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (Number(c) / 4)))
).toString(16)
)
}
// Check for existing Growthbook UUID
// If it exists, refresh the expiration date
const existingUUID = Cookies.get(COOKIE_NAME)
if (existingUUID) {
// Refresh the expiration date when retrieved
Cookies.set(COOKIE_NAME, existingUUID, {
expires: COOKIE_DAYS,
path: "/",
sameSite: "strict",
secure: process.env.NODE_ENV === "production",
})
return existingUUID
}
const uuid = genUUID()
if (uuid) {
Cookies.set(COOKIE_NAME, uuid, {
expires: COOKIE_DAYS,
path: "/",
sameSite: "strict",
secure: process.env.NODE_ENV === "production",
})
return uuid
}
return ""
}
export default getUUID
little-balloon-64875
03/18/2025, 4:36 PMlittle-balloon-64875
03/18/2025, 4:39 PMgbuuid
and anonymous_id
are different.
⢠Experiment Viewed
only fires if Segment has loaded.
⦠This contains the uuid as a property and the anonymous_id
as part of the normal event.fresh-football-47124
fresh-football-47124
little-balloon-64875
03/18/2025, 5:10 PMfresh-football-47124
fresh-football-47124
little-balloon-64875
03/19/2025, 6:51 AM|anonymous_id |variation|timestamp |
|------------------------------------|---------|-----------------------------|
|01453618-0363-4ea1-bb41-e6576a44a6c1|0 |2025-03-17 17:08:12.898 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-17 18:26:25.120 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-17 19:19:17.262 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-17 20:30:38.444 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-17 21:44:30.400 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-17 23:45:35.510 +0000|
|01453618-0363-4ea1-bb41-e6576a44a6c1|1 |2025-03-18 11:57:48.383 +0000|
Here's an example. It's interesting that it showed a 0 variant, but then the rest of the time it's 1.little-balloon-64875
03/19/2025, 7:45 PM{
"anonymousId": "123456",
"userId": "abcdefg",
"properties": {
"userId": "qwerty"
}
}
little-balloon-64875
03/20/2025, 11:05 AMsteep-dog-1694
03/20/2025, 3:14 PM