npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

toggled

v2.1.0

Published

Tiny library to use feature flags on React

Downloads

18

Readme

Tiny library to use feature flags in React. Get features by its slug identifier or get a binary output using flag queries.

Installation

NPM:

npm i toggled

Yarn:

yarn add toggled

API

DefaultFeature Interface

Internal public interface used by default to type the <FeatureProvider /> and useFeature.

Specification

export interface DefaultFeature {
  slug: string
}

Example

// src/types/toggled.d.ts
import 'toggled'

// extend the interface if needed
declare module 'toggled' {
  export interface DefaultFeature {
    slug: string
    settings?: any
  }
}

FlagQuery Type

It could be the feature slug or an flag queries array or more powerful, an object query.

Specification

type FlagQuery =
  | string
  | FlagQuery[]
  | {
      $or: FlagQuery[]
    }
  | {
      $and: FlagQuery[]
    }
  | {
      [slug: string]: boolean
    }

Example

// src/constants/domain.ts
import { Op } from 'toggled'

// Note that each entry is a `FlagQuery`
export const flagQueries: Record<string, FlagQuery> = {
  // True if the slug is in the context
  FF_1: 'ff-1',

  // True if all the slugs are in the context
  FF_2_FULL: ['ff-2.1', 'ff-2.2'],

  // True if `'ff-2.1'` is in the context and `'ff-2.2'` is not
  FF_2_1_ONLY: {
    'ff-2.1': true,
    'ff-2.2': false,
  },

  // True if `'ff-3.1'` **or** `'ff-3.2'` is in the context
  FF_3_X: {
    [Op.OR]: ['ff-3.1', 'ff-3.2'],
  },

  // True if `'ff-4.1'` **and** `'ff-4.2'` are in the context
  FF_4_FULL: {
    [Op.AND]: ['ff-4.1', 'ff-4.2'],
  },

  // True if all the previous queries are true
  COMPLEX: {
    FF_1: 'ff-1',
    FF_2_FULL: ['ff-2.1', 'ff-2.2'],
    FF_2_1_ONLY: {
      'ff-2.1': true,
      'ff-2.2': false,
    },
    FF_3_X: {
      [Op.OR]: ['ff-3.1', 'ff-3.2'],
    },
    FF_4_FULL: {
      [Op.AND]: ['ff-4.1', 'ff-4.2'],
    },
  },
}

FeatureContext

Library context, exported for no specific reason, avoid using it and prefer the custom hooks, or open a PR to add a new one that obligates you to use the FeatureContext.

Specification

interface FeatureContextValue<F extends DefaultFeature = DefaultFeature> {
  cache: Map<string, F>
}

<FeatureProvider />

Provider component that exposes the features in a more convenient way to get them by its own slugs.

Specification

interface FeatureProviderProps<F extends DefaultFeature = DefaultFeature> {
  features: F[]
  children: React.ReactNode
}

Example

import { FeatureProvider } from 'toggled'
import apiClient from './api-client'
import App from './app'

apiClient.getAllFeatures().then(features => {
  ReactDOM.render(
    <FeatureProvider features={features}>
      <App />
    </FeatureProvider>,
    document.getElementById('root'),
  )
})

useFeature

Hook that is used to get a feature object from the context by its slug. Notice that it could be undefined because the context only should contain the features that are enabled.

Specification

interface UseFeature<F extends DefaultFeature = DefaultFeature> {
  (slug: string): F | undefined
}

Example

// src/app.tsx
import { useFeature } from 'toggled'

function App() {
  const themeFF = useFeature('theme')

  return (
    <ThemeProvider theme={themeFF ? themeFF.settings.name : 'default'}>
      <Component />
    </ThemeProvider>
  )
}

useFlagQueryFn

Hook that is used to get the magic function that can process a flag query.

Specification

interface UseFlagQueryFn {
  (): (query: FlagQuery) => boolean
}

Example

import { useFlagQueryFn } from 'toggled'

export default function App() {
  const flagQueryFn = useFlagQueryFn()

  return (
    <Layout designV2={flagQueryFn({ 'design-v2': true, 'design-v1': false })}>
      {flagQueryFn('chat') && <ChatWidget>}
    </Layout>
  )
}

For more use cases, please go to the tests.

useFlag

Hook that is used to get a binary output based on the existence of a feature in the context. So, if the feature is in the context then the flag will be true, otherwise false.

The useFlagQueryFn hook is used internally.

Specification

interface UseFlag {
  (query: FlagQuery): boolean
}

Example

import { useFlag } from 'toggled'

export default function App() {
  const hasChat = useFlag('chat')

  const hasDesignV2Only = useFlag({ 'design-v2': true, 'design-v1': false })

  return (
    <Layout designV2={hasDesignV2Only}>
      {hasChat && <ChatWidget>}
    </Layout>
  )
}

<Flag />

Component to apply conditional rendering using a flagQuery

Specification

interface FlagProps {
  flagQuery: FlagQuery
  children: React.ReactNode
}

Example

import { Flag } from 'toggled'

export default function App() {
  return (
    <Flag flagQuery={{ 'design-v2': true, 'design-v1': false }}>
      <Layout designV2={hasDesignV2Only}>
        <Flag flagQuery="chat">
          <ChatWidget>
        </Flag>
      </Layout>
    </Flag>
  )
}

<Feature />

Component to consume a feature object declaratively instead of useFeature

Specification

export interface FeatureProps {
  slug: string
  children(feature: DefaultFeature): React.ReactElement
}

Example

import { Feature } from 'toggled'

export default function App() {
  return (
    <Feature slug="chat">
      {feature => {
        return <ChatWidget settings={feature.settings}>
      }}
    </Feature>
  )
}

License

MIT © Ricardo Q. Bazan