All Projects → happykit → Flags

happykit / Flags

Licence: mit
⛳️ Feature Flags for Next.js

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Flags

Tweek
Tweek - an open source feature manager
Stars: ✭ 268 (-3.25%)
Mutual labels:  feature-flags, feature-toggles
erlang-server-sdk
LaunchDarkly Server-Side SDK for Erlang/Elixir
Stars: ✭ 16 (-94.22%)
Mutual labels:  feature-flags, feature-toggles
CloudKitFeatureFlags
A library that lets you setup feature flagging for your iOS app using CloudKit
Stars: ✭ 91 (-67.15%)
Mutual labels:  feature-flags, feature-toggles
nestjs-config
NestJS Module for Nonfig services. Nonfig combines Configurations and Features. So you change features, and release swiftly, and measure to digital impact.
Stars: ✭ 40 (-85.56%)
Mutual labels:  feature-flags, feature-toggles
toggler
toggler is a feature flag service to decouple deployment, feature enrollment and experiments
Stars: ✭ 27 (-90.25%)
Mutual labels:  feature-flags, feature-toggles
unleash-client-java
Unleash client SDK for Java
Stars: ✭ 86 (-68.95%)
Mutual labels:  feature-flags, feature-toggles
feature-flag-android
A Gradle plugin to achieve feature flag based development for Android applications.
Stars: ✭ 82 (-70.4%)
Mutual labels:  feature-flags, feature-toggles
ld-redux
A library to integrate launch darkly with react redux
Stars: ✭ 33 (-88.09%)
Mutual labels:  feature-flags, feature-toggles
ruby-server-sdk
LaunchDarkly Server-side SDK for Ruby
Stars: ✭ 25 (-90.97%)
Mutual labels:  feature-flags, feature-toggles
jest-launchdarkly-mock
Easily unit test LaunchDarkly feature flagged components with jest
Stars: ✭ 14 (-94.95%)
Mutual labels:  feature-flags, feature-toggles
laravel-rollout
A package to integrate rollout into your Laravel project.
Stars: ✭ 23 (-91.7%)
Mutual labels:  feature-flags, feature-toggles
ios-client-sdk
LaunchDarkly Client-side SDK for iOS (Swift and Obj-C)
Stars: ✭ 45 (-83.75%)
Mutual labels:  feature-flags, feature-toggles
unleash-docker
Docker container for unleash
Stars: ✭ 89 (-67.87%)
Mutual labels:  feature-flags, feature-toggles
PowerShell-FeatureFlags
PowerShell module containing a Feature Flags implementation based on a local config file.
Stars: ✭ 15 (-94.58%)
Mutual labels:  feature-flags, feature-toggles
eight ball
Ruby gem for querying feature flags
Stars: ✭ 17 (-93.86%)
Mutual labels:  feature-flags, feature-toggles
flagsmith-js-client
Javascript Client for Flagsmith. Ship features with confidence using feature flags and remote config. Host yourself or use our hosted version at https://www.flagsmith.com/
Stars: ✭ 42 (-84.84%)
Mutual labels:  feature-flags, feature-toggles
YMFF
Feature management made easy.
Stars: ✭ 26 (-90.61%)
Mutual labels:  feature-flags, feature-toggles
react-client
React JS SDK client for Split Software
Stars: ✭ 23 (-91.7%)
Mutual labels:  feature-flags, feature-toggles
featurehub
FeatureHub - cloud native feature flags, A/B testing and remote configuration service. Real-time streaming feature updates. Provided with Java, JavaScript, Go, .Net, Android and Flutter SDKs.
Stars: ✭ 136 (-50.9%)
Mutual labels:  feature-flags, feature-toggles
react-client-sdk
LaunchDarkly Client-side SDK for React.js
Stars: ✭ 42 (-84.84%)
Mutual labels:  feature-flags, feature-toggles
Website  •  Full Tutorial  •  Twitter

   

Add Feature Flags to your Next.js application with a single React Hook. This package integrates your Next.js application with HappyKit Flags. Create a free happykit.dev account to get started.

Key Features

  • written for Next.js
  • integrate using a simple useFlags() hook
  • only 1 kB in size
  • extremely fast flag responses (~50ms)
  • supports individual user targeting
  • server-side rendering support
  • static site generation support (redeploy your website on flag changes)


Want to see a demo? HappyKit Flags Demo





Installation

npm install @happykit/flags

Setup

Configure your application in _app.js.

// _app.js
import { configure } from '@happykit/flags';

configure({ clientId: process.env.NEXT_PUBLIC_FLAGS_CLIENT_ID });

If you don't have a custom _app.js yet, see the Custom App section of the Next.js docs for setup instructions.

Create an account on happykit.dev to receive your clientId. You'll find it in the Keys section of your project settings once you created a project.

Make sure the environment variable containing the clientId starts with NEXT_PUBLIC_ so the value is available on the client side.

Store your clientId in .env.local:

# .env.local
NEXT_PUBLIC_FLAGS_CLIENT_ID=flags_pub_development_xxxxxxxxxx

Later on, don't forget to also provide the environment variable in production.

There's also a full walkthrough of the setup, which explains the setup in your project and in HappyKit Flags itself.

Basic Usage

You can load flags on the client with a single useFlags call.

// pages/foo.js
import { useFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags();
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

Or with server-side rendering

// pages/foo.js
import { useFlags, getFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ initialFlags: props.initialFlags });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getServerSideProps = async () => {
  const initialFlags = await getFlags();
  return { props: { initialFlags } };
};

API

configure

  • configure(options)
    • options.clientId (string) required: Your HappyKit Flags Client Id
    • options.defaultFlags (object) optional: Key-value pairs of flags and their values. These values are used as fallbacks in useFlags and getFlags. The fallbacks are used while the actual flags are loaded, in case a flag is missing or when the request loading the flags fails for unexpected reasons. If you don't declare defaultFlags, then the flag values will be undefined.
    • options.disableCache (boolean) optional: Pass true to turn off the client-side cache. The cache is persisted to localStorage and persists across page loads. Even with an enabled cache, all flags will get revalidated in stale-while-revalidate fashion.

useFlags

  • useFlag(options)
    • options.user (object) optional: A user to load the flags for. The user you pass here will be stored in HappyKit for future reference and individual targeting. A user must at least have a key. See the supported user attributes here.
    • options.initialFlags (object) optional: In case you preloaded your flags during server-side rendering using getFlags(), provide the flags as initialFlags. The client will then skip the initial request and use the provided flags instead. This allows you to get rid of loading states on the client.
    • options.revalidateOnFocus (object) optional: By default, the client will revalidate all feature flags when the browser window regains focus. Pass revalidateOnFocus: false to skip this behaviour.

This function returns an object containing the requested flags.

Supported user attributes

Provide any of these attributes to store them in HappyKit. You will be able to use them for targeting specific users based on rules later on (not yet available in HappyKit Flags).

  • key (string) required: Unique key for this user
  • email (string): Email-Address
  • name (string): Full name or nickname
  • avatar (string): URL to users profile picture
  • country (string): Two-letter uppercase country-code of user's county, see ISO 3166-1

getFlags

  • getFlags(user)
    • user (object) optional: A user to load the flags for. The user you pass here will be stored in HappyKit for future reference. A user must at least have a key. See a list of supported user attributes here.

This function returns a promise resolving to an object containing requested flags.

Advanced Usage

With user targeting

You can provide a user as the first argument. Use this to enable per-user targeting of your flags. A user must at least have a key property.

// pages/foo.js
import { useFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ user: { key: 'user-id' } });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}
See here if you're using server-side rendering

Or if you're using prerendering

// pages/foo.js
import { useFlags, getFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({
    user: props.user,
    initialFlags: props.initialFlags,
  });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getServerSideProps = async () => {
  const user = { key: 'user-id' };
  const initialFlags = await getFlags(user);
  return { props: { user, initialFlags } };
};

See all supported user attributes


Configuring application-wide default values

You can configure application-wide default values for flags. These defaults will be used while your flags are being loaded (unless you're using server-side rendering). They'll also be used as fallback values in case the flags couldn't be loaded from HappyKit.

// _app.js
import { configure } from '@happykit/flags';

configure({
  clientId: process.env.NEXT_PUBLIC_FLAGS_CLIENT_ID,
  defaultFlags: { xzibit: true },
});

With initial flag values

Being able to set initial flag values is the first step towards server-side rendering. When you pass in initialFlags the flags will be set from the beginning. This is avoids the first request on the client.

// pages/foo.js
import { useFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ initialFlags: { xzibit: true } });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

With server-side rendering

// pages/foo.js
import { useFlags, getFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ initialFlags: props.initialFlags });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getServerSideProps = async () => {
  const initialFlags = await getFlags();
  return { props: { initialFlags } };
};

With static site generation

// pages/foo.js
import { useFlags, getFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ initialFlags: props.initialFlags });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getStaticProps = () => {
  const initialFlags = await getFlags();
  return { props: { initialFlags } };
};

With static site generation only

You don't even need to use useFlags in case you're regenerating your site on flag changes anyways.

HappyKit can trigger redeployment of your site when you change your flags by calling a Deploy Hook you specify.

// pages/foo.js
import { getFlags } from '@happykit/flags';

export default function FooPage(props) {
  return props.flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getStaticProps = () => {
  const initialFlags = await getFlags();
  return { props: { initialFlags } };
};

The upside of this approach is that useFlags isn't even shipped to the client.

For use with getStaticProps the downside is that the new flags are only available once your site is redeployed. You can automate redeployments on flag changes with Deploy Hooks..

For use with getServerSideProps the downside is that flag changes are only shown when the page is reloaded. You also lose client-side bootstrapping of feature flags, which uses cached flags while requesting the new flags in the background in a stale-while-revaldiate fashion.

With disabled revalidation

  • revalidateOnFocus = true: auto revalidate when window gets focused
// pages/foo.js
import { useFlags, getFlags } from '@happykit/flags';

export default function FooPage(props) {
  const flags = useFlags({ revalidateOnFocus: false });
  return flags.xzibit ? 'Yo dawg' : 'Hello';
}

export const getStaticProps = () => {
  const initialFlags = await getFlags();
  return { props: { initialFlags } };
};

Examples

Full example

This example shows the full configuration with server-side rendering and code splitting.

// _app.js
import App from 'next/app';
import { configure } from '@happykit/flags';

configure({ clientId: process.env.NEXT_PUBLIC_FLAGS_CLIENT_ID });

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}
// pages/profile.js
import * as React from 'react';
import { useFlags, getFlags, Flags } from '@happykit/flags';
import dynamic from 'next/dynamic';

const ProfileVariantA = dynamic(() => import('../components/profile-a'));
const ProfileVariantB = dynamic(() => import('../components/profile-b'));

export default function Page(props) {
  const flags = useFlags({
    user: props.user,
    initialFlags: props.initialFlags,
  });

  return flags.profileVariant === 'A' ? (
    <ProfileVariantA user={props.user} />
  ) : (
    <ProfileVariantB user={props.user} />
  );
}

export const getServerSideProps = async ({ req, res }) => {
  // preload your user somehow
  const user = await getUser(req);
  // pass the user to getFlags to preload flags for that user
  const initialFlags = await getFlags(user);

  return { props: { user, initialFlags } };
};

TypeScript example

Default Types

@happykit/flags includes type definitions. By default, flags returned from useFlags and getFlags have the following type:

type Flags = { [key: string]: boolean | number | string | undefined };

You can use @happykit/flags without further configuration and get pretty good types.

Custom Flag Type

However, all exported functions accept an optional generic type, so you can harden your flag definitions by defining a custom flag type. This allows you to define flag values explicitily.

// _app.tsx
import { configure } from '@happykit/flags';

type Flags = {
  booleanFlag: boolean;
  numericFlag: number;
  textualFlag: string;
  // You can lock textual and numeric flag values down even more, since
  // you know all possible values:
  // numericFlag: 0 | 10;
  // textualFlag: 'profileA' | 'profileB';
};

// the types defined in "configure" are used to check "defaultFlags"
configure<Flags>({
  endpoint: 'http://localhost:8787/',
  clientId: 'flags_pub_272357356657967622',
  defaultFlags: {
    booleanFlag: true,
    numericFlag: 10,
    textualFlag: 'profileA',
  },
});
// pages/SomePage.tsx
import { useFlags, getFlags } from '@happykit/flags';

type Flags = {
  booleanFlag: boolean;
  numericFlag: number;
  textualFlag: string;
};

export default function SomePage(props) {
  const flags = useFlags<Flags>({ initialFlags: props.flags });
  flags.booleanFlag; // has type "boolean"
  flags.numericFlag; // has type "number"
  flags.textualFlag; // has type "string"
  return <div>{JSON.stringify(flags, null, 2)}</div>;
}

export async function getServerSideProps() {
  const initialFlags = await getFlags<Flags>();

  initialFlags.booleanFlag; // has type "boolean"
  initialFlags.numericFlag; // has type "number"
  initialFlags.textualFlag; // has type "string"

  return { props: { initialFlags } };
}

Code splitting

If you have two variants for a page and you only want to render one depending on a feature flag, you're able to keep the client-side bundle small by using dynamic imports.

import * as React from 'react';
import { useFlags, getFlags } from '@happykit/flags';
import dynamic from 'next/dynamic';

const ProfileVariantA = dynamic(() => import('../components/profile-a'));
const ProfileVariantB = dynamic(() => import('../components/profile-b'));

export default function Page(props) {
  const flags = useFlags({ user: { key: 'user_id_1' } });

  // display nothing while we're loading
  if (flags.profileVariant === undefined) return null;

  return flags.profileVariant === 'A' ? (
    <ProfileVariantA user={props.user} />
  ) : (
    <ProfileVariantB user={props.user} />
  );
}

You can even go one step further and preload the flags on the server, so that the client receives a prerenderd page.

Notice that the loading state is gone with that as well, since the flags are available upon the first render.

// with server-side flag preloading
import * as React from 'react';
import { useFlags, getFlags, Flags } from '@happykit/flags';
import dynamic from 'next/dynamic';

const ProfileVariantA = dynamic(() => import('../components/profile-a'));
const ProfileVariantB = dynamic(() => import('../components/profile-b'));

export default function Page(props) {
  const flags = useFlags({
    user: props.user,
    initialFlags: props.initialFlags,
  });

  return flags.profileVariant === 'A' ? (
    <ProfileVariantA user={props.user} />
  ) : (
    <ProfileVariantB user={props.user} />
  );
}

export const getServerSideProps = async ({ req, res }) => {
  // preload your user somehow
  const user = await getUser(req);
  // pass the user to getFlags to preload flags for that user
  const initialFlags = await getFlags(user);

  return { props: { user, initialFlags } };
};
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].