Case Study : How to avoid flickering with NextJS SSR

When the SDK has not yet fetched the flags from Flagship, getFlag will return the default value first and the flag value once fetchFlags is complete. Based on that, the user will see first the default value of the flag and then the value returned by Flagship.
This flickering may have an impact on the user experience.

In this example, we will guide you to avoid the flag value flickering with NextJS SSR.

Follow this link to setup a NextJS project

Once your project is set up, you need to install and initialize the Flagship React SDK

yarn add @flagship.io/react-sdk

We will customize the next component App in order to initialize the Flagship SDK.

On the server-side of the custom _app component, you will:

  • fetch the flags for the targeted visitor
  • pass the fetched flags to the page via props

To avoid the flickering issue, we provide you the initialFlagsData props of the FlagshipProvider component. By using this, at first the initialization, the SDK will have a set of default flags if the process of fetching them fails or is incomplete.

// pages/_app.jsx

import "../styles/globals.css";
import { Flagship, FlagshipProvider } from "@flagship.io/react-sdk";
import { ENV_ID, API_KEY } from "../config";
import React from "react";
import App from "next/app";

function MyApp({ Component, pageProps, initialFlagsData, initialVisitorData }) {
  return (
    <FlagshipProvider
      visitorData={initialVisitorData}
      initialFlagsData={initialFlagsData} //  set initial flags fetched server side
      envId={ENV_ID}
      apiKey={API_KEY}
    >
      <Component {...pageProps} />
    </FlagshipProvider>
  );
}

MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext);

  //Start the Flagship SDK
  const flagship = Flagship.start(ENV_ID, API_KEY, {
    fetchNow: false,
  });

  const initialVisitorData = {
    id: "my_visitor_id",
    context: {
      any: "value",
    },
  };

  // Create a new visitor
  const visitor = flagship?.newVisitor({
    visitorId: initialVisitorData.id,
    context: initialVisitorData.context,
  });

  //Fetch flags
  await visitor?.fetchFlags();

  // Pass data to the page via props
  return {
    ...appProps,
    initialFlagsData: visitor?.getFlagsDataArray(),
    initialVisitorData,
  };
};

export default MyApp;
// pages/_app.tsx

import "../styles/globals.css";
import type { AppContext, AppProps } from "next/app";
import { Flagship, FlagshipProvider } from "@flagship.io/react-sdk";
import { ENV_ID, API_KEY } from "../config";
import React from "react";
import App from "next/app";

function MyApp({
  Component,
  pageProps,
  initialFlagsData,
  initialVisitorData,
}: AppProps) {
  return (
    <FlagshipProvider
      visitorData={initialVisitorData}
      initialFlagsData={initialFlagsData}
      envId={ENV_ID}
      apiKey={API_KEY}
    >
      <Component {...pageProps} />
    </FlagshipProvider>
  );
}

MyApp.getInitialProps = async (appContext: AppContext) => {
  const appProps = await App.getInitialProps(appContext);

  //Start the Flagship SDK
  const flagship = Flagship.start(ENV_ID, API_KEY, {
    fetchNow: false,
  });

  // set your target visitor data
  const initialVisitorData = {
    id: "my_visitor_id", 
    context: {
      any: "value", 
    },
  };

  // Create a new visitor
  const visitor = flagship?.newVisitor({
    visitorId: initialVisitorData.id,
    context: initialVisitorData.context,
  });

  //Fetch flags
  await visitor?.fetchFlags();

  // Pass data to the page via props
  return {
    ...appProps,
    initialFlagsData: visitor?.getFlagsDataArray(),
    initialVisitorData,
  };
};

export default MyApp;

On the index page, you'll now get the flag without having any flickering

// pages/index.jsx

import { HitType, useFlagship, useFsFlag } from "@flagship.io/react-sdk";
import styles from "../styles/Home.module.css";

const Index = () => {
  const fs = useFlagship();

  //get flag 
  const btnColorFlag = useFsFlag("my_flag_key", "default-value");

  const onSendHitClick = () => {
    fs.hit.sendMultiple([
      { type: HitType.PAGE_VIEW, documentLocation: "abtastylab" },
      { type: HitType.SCREEN, documentLocation: "abtastylab" },
    ]);
  };

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        
        <h1>Avoid flickering with NextJs SSR</h1>
        
        <p>flag key: my_flag_key</p>
        <p>flag value: {btnColorFlag.getValue()}</p>

        <button
          style={{ width: 100, height: 50 }}
          onClick={() => {
            onSendHitClick();
          }}
        >
          {" "}
          Send hits
        </button>
      </main>
    </div>
  );
};

export default Index;
// pages/index.tsx

import { HitType, useFlagship, useFsFlag } from "@flagship.io/react-sdk";
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

const Index: NextPage = () => {
  const fs = useFlagship();

  const btnColorFlag = useFsFlag("js-qa-app", "default");

  const onSendHitClick = () => {
    fs.hit.sendMultiple([
      { type: HitType.PAGE_VIEW, documentLocation: "abtastylab" },
      { type: HitType.SCREEN, documentLocation: "abtastylab" },
    ]);
  };

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <p>flag key: js-qa-app</p>
        <p>value: {btnColorFlag.getValue()}</p>

        <button
          style={{ width: 100, height: 50 }}
          onClick={() => {
            onSendHitClick();
          }}
        >
          {" "}
          Send hits
        </button>
      </main>
    </div>
  );
};

export default Index;

Any feedback?
Want another tutorial?
Contact us

See full code here : github


Did this page help you?