Skip to main content

Feature Flag Helpers

Feature flag management utilities for checking and managing feature availability in the WorkPayCore frontend application.

Core Function​

isFeatureFlagEnabled(featureFlags)​

Checks if one or more feature flags are enabled for the current company.

Parameters:

  • featureFlags (TFeatFlagValues | TFeatFlagValues[]): Single feature flag or array of feature flags to check

Returns:

  • boolean: True if the feature flag(s) are enabled

Example:

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

// Check single feature flag
const isTimeAttendanceV2Enabled = isFeatureFlagEnabled(FEATURE_FLAGS.TA_V2);

if (isTimeAttendanceV2Enabled) {
// Show new time attendance interface
return <TimeAttendanceV2 />;
}

// Check multiple feature flags (returns true if ANY are enabled)
const hasPaymentFeatures = isFeatureFlagEnabled([
FEATURE_FLAGS.PAYMENTS_V2,
FEATURE_FLAGS.wallet_pin_reset,
]);

if (hasPaymentFeatures) {
// Show payment features
return <PaymentModule />;
}

Use Cases:

  • Conditional component rendering
  • Feature availability checking
  • A/B testing implementation
  • Gradual feature rollout

Integration Patterns​

React Component Wrapper​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

interface FeatureGateProps {
flag: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}

const FeatureGate: React.FC&lt;FeatureGateProps&gt; = ({
flag,
children,
fallback = null,
}) => {
const isEnabled = isFeatureFlagEnabled(flag);

return isEnabled ? <>{children}</> : <>{fallback}</>;
};

// Usage
const PayrollInterface = () => (
<div>
<h1>Payroll</h1>

<FeatureGate
flag={FEATURE_FLAGS['revamped-run-payroll-interface']}
fallback={<LegacyPayrollInterface />}
>
<NewPayrollInterface />
</FeatureGate>
</div>
);

Custom Hook​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';

const useFeatureFlag = (flag: string) => {
const [isEnabled, setIsEnabled] = useState(false);
const [loading, setLoading] = useState(true);

useEffect(() => {
try {
const enabled = isFeatureFlagEnabled(flag);
setIsEnabled(enabled);
} catch (error) {
console.error('Feature flag check failed:', error);
setIsEnabled(false);
} finally {
setLoading(false);
}
}, [flag]);

return { isEnabled, loading };
};

// Usage
const PaymentComponent = () => {
const { isEnabled: isPaymentsV2Enabled, loading } = useFeatureFlag(
FEATURE_FLAGS.PAYMENTS_V2,
);

if (loading) return <Spinner />;

return isPaymentsV2Enabled ? <PaymentsV2 /> : <LegacyPayments />;
};

Multiple Flag Checking​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

const useMultipleFeatureFlags = (flags: string[]) => {
const [enabledFlags, setEnabledFlags] = useState<Record<string, boolean>>({});

useEffect(() => {
const flagStates = flags.reduce(
(acc, flag) => {
acc[flag] = isFeatureFlagEnabled(flag);
return acc;
},
{} as Record<string, boolean>,
);

setEnabledFlags(flagStates);
}, [flags]);

return enabledFlags;
};

// Usage
const AdminDashboard = () => {
const flagStates = useMultipleFeatureFlags([
FEATURE_FLAGS.employee_expense_report,
FEATURE_FLAGS.multi_category_expense,
FEATURE_FLAGS.revamped_payslip,
]);

return (
<div>
<h1>Admin Dashboard</h1>

{flagStates[FEATURE_FLAGS.employee_expense_report] && (
<EmployeeExpenseReportWidget />
)}

{flagStates[FEATURE_FLAGS.multi_category_expense] && (
<MultiCategoryExpenseWidget />
)}

{flagStates[FEATURE_FLAGS.revamped_payslip] && <NewPayslipPreview />}
</div>
);
};

Advanced Usage Examples​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

const useFeatureBasedRoutes = () => {
const routes = useMemo(() => {
const baseRoutes = [
{ path: '/dashboard', component: Dashboard },
{ path: '/employees', component: Employees },
];

// Conditionally add routes based on feature flags
if (isFeatureFlagEnabled(FEATURE_FLAGS.TA_V2)) {
baseRoutes.push({
path: '/time-attendance',
component: TimeAttendanceV2,
});
} else {
baseRoutes.push({
path: '/time-attendance',
component: LegacyTimeAttendance,
});
}

if (isFeatureFlagEnabled(FEATURE_FLAGS.PAYMENTS_V2)) {
baseRoutes.push({
path: '/payments',
component: PaymentsV2,
});
}

return baseRoutes;
}, []);

return routes;
};

Feature Flag Middleware​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';

const createFeatureFlagMiddleware = (
flag: string,
fallbackComponent: React.ComponentType,
) => {
return (WrappedComponent: React.ComponentType) => {
const WithFeatureFlag = (props: any) => {
const isEnabled = isFeatureFlagEnabled(flag);

if (!isEnabled) {
return <fallbackComponent {...props} />;
}

return <WrappedComponent {...props} />;
};

WithFeatureFlag.displayName = `WithFeatureFlag(${
WrappedComponent.displayName || WrappedComponent.name
})`;

return WithFeatureFlag;
};
};

// Usage
const withPaymentsV2 = createFeatureFlagMiddleware(
FEATURE_FLAGS.PAYMENTS_V2,
LegacyPayments,
);

const PaymentsPage = withPaymentsV2(PaymentsV2);

API Integration​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

const createApiClient = () => {
const baseConfig = {
baseURL: '/api/v1',
headers: {
'Content-Type': 'application/json',
},
};

// Add feature flag headers for backend
const enabledFlags = Object.values(FEATURE_FLAGS).filter(flag =>
isFeatureFlagEnabled(flag),
);

if (enabledFlags.length > 0) {
baseConfig.headers['X-Feature-Flags'] = enabledFlags.join(',');
}

return axios.create(baseConfig);
};

// Usage
const apiClient = createApiClient();

// The backend can now adjust responses based on enabled feature flags
const fetchUserData = () => apiClient.get('/users');

Form Field Conditional Rendering​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';

const ExpenseForm = () => {
const isMultiCategoryEnabled = isFeatureFlagEnabled(
FEATURE_FLAGS.multi_category_expense,
);

return (
<form>
<input name='description' placeholder='Description' />
<input name='amount' type='number' placeholder='Amount' />

{isMultiCategoryEnabled ? (
<MultiCategorySelect name='categories' />
) : (
<SingleCategorySelect name='category' />
)}

<button type='submit'>Submit Expense</button>
</form>
);
};

Analytics Integration​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';
import { analyticsTrackEvent } from '@/utils/helpers/analytics';

const trackFeatureUsage = (feature: string, action: string) => {
if (isFeatureFlagEnabled(feature)) {
analyticsTrackEvent('feature_usage', {
feature,
action,
enabled: true,
timestamp: new Date().toISOString(),
});
}
};

// Usage
const PayrollComponent = () => {
const handleRunPayroll = () => {
trackFeatureUsage(
FEATURE_FLAGS['revamped-run-payroll-interface'],
'run_payroll_clicked',
);

// Run payroll logic
};

return <button onClick={handleRunPayroll}>Run Payroll</button>;
};

Error Handling​

Safe Feature Flag Checking​

import { isFeatureFlagEnabled } from '@/utils/helpers/feature-flag';

const safeFeatureFlagCheck = (flag: string, defaultValue = false) => {
try {
return isFeatureFlagEnabled(flag);
} catch (error) {
console.error(`Feature flag check failed for ${flag}:`, error);
return defaultValue;
}
};

// Usage with fallback
const ExpenseReporting = () => {
const hasEmployeeReports = safeFeatureFlagCheck(
FEATURE_FLAGS.employee_expense_report,
false, // Default to disabled if check fails
);

return (
<div>
<h2>Expense Reports</h2>

{hasEmployeeReports && <EmployeeExpenseButton />}
</div>
);
};

Data Flow​

How Feature Flags Work​

  1. Query Client: Uses React Query to fetch enabled features from API
  2. Cache: Enabled features are cached in memory via queryClient
  3. Check Function: isFeatureFlagEnabled reads from the cache
  4. Array Support: Can check single flag or array of flags
  5. Boolean Logic: Returns true if ANY flag in array is enabled

Data Structure​

// Example cached data structure
const cachedFeatureFlags = {
data: {
data: [
{ id: 1, code: 'revamped-time-and-attendance', enabled: true },
{ id: 2, code: 'revamped-payouts-module', enabled: false },
{ id: 3, code: 'wallet-pin-reset', enabled: true },
],
},
};

Performance Considerations​

  • Caching: Feature flags are cached to avoid repeated API calls
  • Synchronous: Flag checking is synchronous once data is loaded
  • Memory: Minimal memory footprint with simple array operations
  • Re-renders: Use useMemo for expensive flag combinations

Best Practices​

  1. Default Behavior: Always provide fallback for disabled features
  2. Error Handling: Wrap flag checks in try-catch blocks
  3. Testing: Test both enabled and disabled states
  4. Documentation: Document which flags affect which features
  5. Cleanup: Remove flag checks after full rollout


TypeScript Definitions​

import { TFeatFlagValues } from '@/utils/constants/feature-flags';

export function isFeatureFlagEnabled(
featureFlags: TFeatFlagValues | TFeatFlagValues[],
): boolean;

Dependencies​

  • React Query: For caching feature flag data
  • Feature Flag API: Backend service for flag management
  • Feature Flag Constants: For type-safe flag references