Are you looking to implement feature flags swiftly within your Vite-powered applications? Feature flags, also known as feature toggles, allow developers to toggle features on and off without changing code, enabling incremental feature releases and controlled feature rollouts. In this tutorial, we'll walk you through the process of setting up feature flags using Vite, the next-generation frontend tooling framework.
Setting Up Your Environment
Before diving into the implementation details, let's ensure our environment is properly configured. We'll be using Vite along with React for this demonstration.
Firstly, ensure you have Vite installed in your project:
npm install vite --save-dev
Next, let's define our feature flags in the env.json
file:
{
"development": {
"features": {
"counter": {
"enabled": true
},
"react_logo": {
"enabled": true
}
}
},
"production": {
"features": {
"counter": {
"enabled": false
},
"react_logo": {
"enabled": true
}
}
}
}
Here, we specify the status of each feature in different environments, such as development and production. Now, let's set up our vite.config.ts
file and include env.json
configuration there. As we're using TypeScript, we will extract type Env
as union of strings representing each environment in the JSON config. For now that is "development"
and "production"
.
// vite.config.ts
import { defineConfig } from 'vite'
import React from '@vitejs/plugin-react-swc'
import envConfig from './env.json'
type Env = keyof typeof envConfig
export default defineConfig(({ mode }) => {
return {
plugins: [React()],
define: {
__FEATURES__: JSON.stringify(envConfig[mode as Env].features)
}
}
})
Validating Features Across Environments
Before we continue to front-end stuff, we need to make sure that each of our environments has the same features. That means if we have configuration that looks like the one below, our build should break before it reaches an environment. Otherwise, we will have runtime errors aka bugs in production. 🤭
{
"development": {
"features": {
"counter": {
"enabled": true
},
"react_logo": {
"enabled": true
},
"hi_mom": {
"enabled": true
}
}
},
"production": {
"features": {
"counter": {
"enabled": false
},
"react_logo": {
"enabled": true
}
}
}
}
To do that, we won't be including new 3rd party library. Instead, we will do it by ourselves with just a few extra lines of code in our vite.config.ts
. First, we will extract list of features defined under "production"
property and compare all other peer-level properties — environments — whether they have the same list as production. If not, we should log the error in the console and break the build process.
// vite.config.ts
import { defineConfig } from 'vite'
import React from '@vitejs/plugin-react-swc'
import envConfig from './env.json'
type Env = keyof typeof envConfig
const featuresListProd = Object.keys(envConfig.production.features)
const allEnvsMatchProdFeatures = Object.keys(envConfig).every((env) => {
const currentFeaturesList = Object.keys(envConfig[env as Env].features)
return JSON.stringify(currentFeaturesList) === JSON.stringify(featuresListProd)
})
export default defineConfig(({ mode }) => {
if (!allEnvsMatchProdFeatures) {
console.error('\x1b[31m', "Features don't match across all environments.")
process.exit(1)
}
return {
plugins: [React()],
define: {
__FEATURES__: JSON.stringify(envConfig[mode as Env].features)
}
}
})
We have now ensured that features are consistent across different environments. You may have noticed that we used the define
property from Vite config. That allows us to declare global property __FEATURES__
which compiles as window.__FEATURES__
in production build.
Implementing Feature Flags
We've laid the groundwork, and now it's time to implement feature flags in our application components. We'll create a custom hook useFeature.hook.ts
.
// useFeature.hook.ts
const useFeature = (feature: string): boolean => {
return __FEATURES__[feature]?.enabled === true
}
export default useFeature
This simple basically-one-liner hook allows us to query the status of a feature based on its definition in env.json
and return flag that tells us if the feature is enabled.
Next, let's create, another yet, simple component Feature.tsx
to conditionally render features based on their status.
// Feature.tsx
import useFeature from './useFeature.hook'
type FeatureProps = {
flag: string
children: React.ReactNode | React.ReactNode[]
}
const Feature = ({ flag, children }: FeatureProps) => {
const isFeatureEnabled = useFeature(flag)
return isFeatureEnabled ? <>{children}</> : null
}
export default Feature
Congratulations! You've successfully integrated feature flags into your Vite-powered application. 🎉
Exploring Further Possibilities
With feature flags, you gain greater control over feature releases, enabling smoother deployments and user experiences.
In this tutorial, we've covered setting up environment configurations, defining static feature flags and implementing feature flag logic within components. Experiment with different features and environments to see how feature flags can enhance your development workflow.
Next level would be real-time feature flags managed through an SDK in live application. Stay tuned for more insights and tutorials on modern frontend development techniques. Happy coding!
Check out the code