Skip to main content

Feature Flags

The feature-flag system lets you enable/disable features, lock API versions, warn about deprecations, and flip a global maintenance mode across every app at once — all driven by a single Firestore document (zaions_feature_flags). Apps read it; the admin writes it.

Why use feature flags

  • Version control — roll out breaking changes gradually instead of all at once.
  • Feature toggles — turn a feature on or off everywhere without redeploying apps.
  • Deprecation warnings — tell apps to upgrade before you force them to.
  • Maintenance mode — show a maintenance screen across the whole portfolio from one switch.
  • Targeting — enable a feature only for specific platforms or projects.

Check overall status

import { useFeatureFlags } from 'shared-features';

function App() {
const {
status,
loading,
isFeatureAvailable,
hasDeprecatedFeatures,
hasUpgradeRequired,
} = useFeatureFlags();

if (loading) return <Spinner />;

if (status?.maintenanceMode) {
return <MaintenancePage message={status.maintenanceMessage} />;
}

if (isFeatureAvailable('campaigns')) {
return <Promotions />;
}

return <App />;
}

useFeatureFlags(options?) accepts { autoRefresh?, refreshInterval?, autoFetch? } and returns status, loading, error, refetch, isFeatureAvailable(id), getFeatureAvailability(id), hasDeprecatedFeatures, and hasUpgradeRequired.

Check a single feature

import { useFeature } from 'shared-features';

function ContactSection() {
const { available, loading, deprecated, deprecationWarning } = useFeature('contactInfo');

if (loading) return <Spinner />;
if (!available) return <LegacyContactInfo />;
if (deprecated) logger.warn(deprecationWarning);

return <NewContactInfo />;
}

useFeature(featureId) returns available, loading, enabled, deprecated, upgradeRequired, deprecationWarning, and unavailableReason.

Conditional rendering with a gate

import { useFeatureGate } from 'shared-features';

function MyComponent() {
const { shouldRender, FallbackOrChildren } = useFeatureGate('socialLinks');

return (
<FallbackOrChildren fallback={<OldSocialLinks />}>
<NewSocialLinks />
</FallbackOrChildren>
);
}

React to changes in real time

import { useFeatureFlagsSubscription } from 'shared-features';

useFeatureFlagsSubscription((status) => {
if (status?.maintenanceMode) showMaintenanceBanner(status.maintenanceMessage);
});

There is also useSharedFeaturesOperational() for a quick boolean "is shared-features working right now" check.

Available feature ids

Feature idDescription
campaignsAdvertising campaigns
broadcastsBroadcast notifications
contactInfoContact information
developerInfoDeveloper information
socialLinksSocial media links
paymentOptionsPayment methods
addressInfoAddress information
servicesProfessional services
skillsSkills display
testimonialsClient testimonials
projectsPortfolio projects
note

Whether each id is "enabled" at runtime is decided by the admin's zaions_feature_flags document — it is not hard-coded in the package. A feature showing as unavailable means the admin has not enabled it, not that the code is missing.

The version-locking lifecycle

  1. The admin bumps a feature's version in zaions_feature_flags.
  2. Apps still on the old version get deprecation warnings (deprecated: true).
  3. Once the admin bumps minVersion past the app's version, the app gets upgradeRequired: true.
  4. The app updates its code and bumps featureVersions in initSharedFeatures().
initSharedFeatures({
// ...firebaseConfig, projectId, projectName, platform
featureVersions: {
campaigns: 1,
broadcasts: 1,
contactInfo: 2, // moved to v2 API
},
});

FAQ

What happens if the flags document does not exist yet? The hooks resolve to a safe default — features behave as unavailable rather than erroring. Use initializeFeatureFlags() (a service helper) from the admin side to seed it.

Does checking a flag cost a Firestore read every render? No. Flags are fetched once and cached; useFeatureFlagsSubscription uses a single realtime listener. Use clearFeatureFlagsCache() to force a refresh.

Can I gate by platform? Yes — the admin can scope a feature to specific platforms/projects, and the platform/projectId you pass to initSharedFeatures() is what the flag is evaluated against.

See the hooks reference and services reference for full signatures.