Feature Flags

Expose features to your users using feature flags.

Feature flags allow you to toggle functionality on/off during runtime and give people access to certain features based on their attributes, like their role or the current billing plan.

Import

  • FeaturesProvider: The context provider that manages state.
  • Has: Conditionally render components.
  • useFlag: Hook that returns a flag value.
  • useFeatures: Returns the features context.
  • useHasFeature: Hook used to check if user has one or more features.
import { FeaturesProvider, Has, useFlag } from '@saas-ui-pro/feature-flags'

Setup

Add FeaturesProvider in the root of you application, to make sure the features context is available throughout your app. The options can either be hardcoded or preferably loaded from your database, so it can be updated on the fly.

The provider options contain segments with attributes (attr) that can be matched against the identify user, more on this below. A user can match multiple segments, and segments can have multiple features. A feature can be either a string or Feature object with a id and value. Strings will be parsed as booleans, while feature objects support custom values.

Configuration

Here is an example config.

import { FeaturesProvider, FeaturesOptions } from '@saas-ui-pro/feature-flags'
const options: FeaturesOptions = {
segments: [
{
id: 'admin',
attr: [
{
key: 'role',
value: 'admin',
},
],
features: ['settings', { id: 'value-feature', value: 'enabled' }],
},
{
id: 'proPlan',
attr: [
{
key: 'plan',
value: 'pro',
},
],
features: ['inbox'],
},
],
}
export default function App({ children }) {
return <FeaturesProvider value={options}>{chilren}</FeaturesProvider>
}

Identify a user

Identifying a user will match their attributes against the configured segments.

Use the useFeatures hook to get access to the features context and create a hook to identify the user. The user object is typically accessible through your AuthProvider using the useAuth hook. If you need additional attributes you can add those as well.

Then add a useEffect hook to call identify like to example below.

import { useFeatures } from '@saas-ui-pro/feature-flags'
import { useAuth } from '@saas-ui/auth'
export default function Layout({ children }) {
const features = useFeatures()
const { user } = useAuth()
// const user = {
// id: 1,
// plan: 'pro',
// role: 'admin'
// }
React.useEffect(() => {
if (features.isReady && user) {
features.identify(user)
}
}, [features.isReady, user])
return <>{children}</>
}

Usage

Basic example

import { Button, Stack, Text } from '@chakra-ui/react'
import { useFeatures } from '@saas-ui-pro/feature-flags'
export default function App() {
const features = useFeatures()
const [plan, setPlan] = React.useState('basic')
const user = React.useMemo(() => {
return {
id: 1,
role: 'admin',
plan,
}
}, [plan])
const upgrade = () => {
setPlan('pro')
}
const downgrade = () => {
setPlan('basic')
}
React.useEffect(() => {
if (features.isReady) {
features.identify(user)
}
}, [features.isReady, user])
return (
<Stack>
{plan === 'pro' ? (
<Button onClick={downgrade}>Downgrade</Button>
) : (
<Button onClick={upgrade}>Upgrade</Button>
)}
<Text>Features: {Object.keys(features.flags).join(', ')}</Text>
</Stack>
)
}

Has

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has feature="settings">
<Button colorScheme="green">Settings Enabled</Button>
</Has>
)
}

Has with fallback

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has
feature="beta-feature"
fallback={<Button colorScheme="red">Enable beta features</Button>}
>
<Button colorScheme="green">Disable beta features</Button>
</Has>
)
}

Has with multiple features

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has
feature={['settings', 'enterprise-feature']}
fallback={<Button colorScheme="red">Feature Disabled</Button>}
>
<Button colorScheme="green">Feature Enabled</Button>
</Has>
)
}

Has with exact features

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has
feature={['settings', 'beta']}
exact={false}
fallback={<Button colorScheme="red">Feature Disabled</Button>}
>
<Button colorScheme="green">Feature Enabled</Button>
</Has>
)
}

Has with value

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has feature="value-feature" value="enabled">
<Button colorScheme="green">Feature Enabled</Button>
</Has>
)
}

Has with multiple values

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has
feature={['settings', 'value-feature']}
value="enabled"
fallback={<Button colorScheme="red">Feature Disabled</Button>}
>
<Button colorScheme="green">Feature Enabled</Button>
</Has>
)
}

Has with render prop

import { Button } from '@chakra-ui/react'
import { Has } from '@saas-ui-pro/feature-flags'
export default function App() {
return (
<Has feature={['settings']}>
{({ flags }) => {
const features = Object.keys(flags)
return (
<Button colorScheme="green">Feature Enabled: {features[0]}</Button>
)
}}
</Has>
)
}

useFlag

import { Card, Button } from '@chakra-ui/react'
import { PropertyList, Property } from '@saas-ui/react'
import { useFlag } from '@saas-ui-pro/feature-flags'
export default function Settings() {
return (
<Card px="4">
<PropertyList>
<Property label="settings" value={String(useFlag('settings'))} />
<Property label="beta" value={String(useFlag('beta'))} />
<Property label="inbox" value={String(useFlag('inbox'))} />
<Property
label="value-feature"
value={String(useFlag('value-feature'))}
/>
</PropertyList>
</Card>
)
}

useHasFeature

import { Box } from '@chakra-ui/react'
import { EmptyState } from '@saas-ui/react'
import { useHasFeature } from '@saas-ui-pro/feature-flags'
export default function Settings() {
const enabled = useHasFeature('settings')
if (!enabled) {
return <EmptyState title="No access" />
}
return <Box>Settings</Box>
}

Multiple features

import { Box } from '@chakra-ui/react'
import { EmptyState } from '@saas-ui/react'
import { useHasFeature } from '@saas-ui-pro/feature-flags'
export default function Settings() {
const enabled = useHasFeature(['settings', 'beta'])
if (!enabled) {
return <EmptyState title="No access" />
}
return <Box>Settings</Box>
}

Was this helpful?