Hi team, we're facing flickering issue while using...
# ask-questions
b
Hi team, we're facing flickering issue while using growthbook feature flag: Summary: When using feature flags to toggle between two versions of a component (e.g., V1_Feature and V2_Feature), there's a noticeable flicker. This occurs because the feature flag value is initially false, leading to the default version (V1_Feature) being rendered temporarily before switching to the correct version (V2_Feature) once the flag resolves. React: The
useFeatureValue
hook allows specifying a fallback value, but this fallback is returned even if the flag doesn't exist or has been deleted for a given environment or user—making it unreliable. React Native: The
useFeatureValue
hook briefly returns the fallback value, then switches to false, and only after a delay resolves to the actual flag value. This further exacerbates the flicker. Overall, the current approach makes it difficult to trust the feature flag's initial state, especially during the resolution phase. Expected Behavior: A loading state should be provided while the feature flag value is being resolved. This would enable developers to render a fallback UI (e.g., spinner or skeleton) and avoid rendering any feature version until the flag value is fully determined. Current Behavior: There is no mechanism to detect if the feature flag is still resolving. As a result, the UI often renders the default version prematurely, leading to a visible flicker when the correct version replaces it. Suggested Solution: Expose a loading or isResolving state from the feature flag hook/provider. This would allow developers to wait until the flag value is fully resolved before rendering either component version, avoiding unexpected flickers and improving UX.
s
https://docs.growthbook.io/lib/react#waiting-for-features-to-load did you see this FeaturesReady component? I think it handles this case
b
The current setup doesn't support showing different versions like v1 and v2 based on feature flags. Moreover, it's not just about rendering different components — in many cases, we also need to trigger different functionalities depending on the feature flag.
s
In that section of the docs I shared, it also shows an example if you need more control.
Copy code
const gb = useGrowthBook();

if (gb.ready) {
  // Do something
}
b
It only indicates when it is ready and initialized, but feature flag values still resolve later. During implementation, I realized that attributes play a crucial role in this process. Feature values are resolved only after the appropriate attributes are passed in. However, this behavior applies specifically to feature flags that have targeting rules based on those attributes. Not for the flags that are set to by default true for any environment.
s
Do you're attributes come in much later? The process that takes the most amount of time is the SDK fetching the payload from the server (GrowthBook initializing). Once that happens, though, flag evaluation happens locally and should be almost instant.
b
Yes, attributes come into play later, but they only affect flags that have forced rules based on those attributes, right? For flags that are set to default
true
in any environment, they still take time to resolve. For now, we've implemented a workaround: we check any flag only after resolving an attribute-based flag (like
feature_loaded = true
). But this is just a temporary workaround.
s
It still sounds like something is amiss maybe in the setup, because it probably shouldn't require so much checking/time for resolution. I think it could be around the fact that a flag can have a default value in GrowthBook but also be disabled using the top-level toggle. When the flag is disabled, it's not included in the feature flag payload at all and resolves to
null
which triggers the fallback value.
b
It's not the case our top level toggle is enabled but still we having delay.
s
Do you want to share more about your implementation, to see if there's anything to debug?
b
• We have created a Provider and there we created a instance of growhbook.
Copy code
const [gb] = useState(() => {
    return new GrowthBook({
      enableDevMode: import.meta.env.DEV,
      clientKey: import.meta.env.VITE_GROWTHBOOK_CLIENT_KEY,
      decryptionKey: import.meta.env.VITE_GROWTHBOOK_DECRYPTION_KEY,
    });
  });
• then we initialize the growthbook after we get growthbook config from applicaiton API.
Copy code
useEffect(() => {
    if (growthbookConfig) {
      gb.init({ payload: growthbookConfig, streaming: false });
    }
  }, [growthbookConfig]);
• We set attributes and config on useEffect when we get values
Copy code
useEffect(() => {
    if (appSettings && appSettings?.growthbookConfig) {
      setgrowthbookConfig(appSettings?.growthbookConfig);
    }
  }, [appSettings]);

  useEffect(() => {
    if (isAuthLoaded && growthbookConfig) {
      if(isAuthenticated && areUserSettingsLoaded) return;
      gb.setAttributes({
        applicationId,
        id: onlyUserId,
        guestUserId,
      });
    }
  }, [
    growthbookConfig,
    isAuthLoaded,
    isAuthenticated,
    areUserSettingsLoaded,
    onlyUserId,
  ]);
Then we just pass that instance on provider and use it through hook on different places. We show loader on application level until we get response from our application api for growhtbook and other configurations.