Offline mode allows your application to continue evaluating feature flags even when the network is unavailable. This is especially important for mobile applications, applications that work offline, or when you want to ensure consistent user experiences during connectivity issues.
Offline mode is disabled by default. To enable it, set the enableOfflineMode
option to true
when initializing the Flagdeck client:
import { Flagdeck } from '@flagdeck/js';
const flagdeck = new Flagdeck({
apiKey: 'your-api-key',
enableOfflineMode: true
});
With offline mode enabled, the SDK:
The process is entirely transparent to your application code. Flag evaluations behave the same way regardless of connectivity state.
The SDK uses an optimized storage system with the following features:
The SDK automatically selects the appropriate storage mechanism based on the environment:
localStorage
with memory fallback~/.flagdeck
) with temp directory fallbackAsyncStorage
when availableYou can manually save flag values for offline use:
// Save a single flag
await flagdeck.saveForOffline('feature-a', true);
// Save multiple flags at once
await flagdeck.saveMultipleForOffline({
'feature-a': true,
'feature-b': false,
'limit': 100
});
To clear all stored flags:
await flagdeck.clearOfflineStorage();
A common pattern is to bootstrap your application with a set of offline values at startup:
async function initializeFlagdeck(userContext) {
// Create the client
const flagdeck = new Flagdeck({
apiKey: 'your-api-key',
enableOfflineMode: true
});
try {
// Fetch all flags needed by your application
const values = await flagdeck.getValues([
'feature-a',
'feature-b',
'limit-value',
'experiment-variant'
], userContext);
// Save for offline use
await flagdeck.saveMultipleForOffline(values);
console.log('Flags bootstrapped for offline use');
} catch (error) {
console.error('Failed to bootstrap flags:', error);
// Application can still proceed - existing offline values will be used if available
}
return flagdeck;
}
// Usage
const flagdeck = await initializeFlagdeck(userContext);
For browser applications, it's important to flush storage and analytics before the app closes:
// Ensure flags are properly saved and analytics are sent before app closes
window.addEventListener('beforeunload', async () => {
// Save important flags
await flagdeck.saveMultipleForOffline({
'critical-feature': await flagdeck.getValue('critical-feature'),
'user-settings': await flagdeck.getValue('user-settings')
});
// Flush analytics
await flagdeck.flushAnalytics();
});
The SDK includes built-in storage management to handle limits efficiently:
These limits help prevent storage issues, especially in browser environments with strict localStorage quotas.
Even with offline mode, always provide default values for critical flags:
// Default value will be used if both network and offline storage fail
const limit = await flagdeck.getValue('api-rate-limit', context, 50);
For applications that run for extended periods offline, implement periodic synchronization:
async function synchronizeFlags(userContext) {
try {
// Get the latest values for critical flags
const values = await flagdeck.getValues([
'feature-a',
'feature-b',
'config-value'
], userContext);
// Save for offline use
await flagdeck.saveMultipleForOffline(values);
console.log('Flags synchronized successfully');
} catch (error) {
console.warn('Could not synchronize flags:', error);
}
}
// Call when the application comes online
networkStatusMonitor.onConnectionRestored(() => {
synchronizeFlags(currentUserContext);
});
// Or on a periodic schedule when online
setInterval(() => {
if (navigator.onLine) {
synchronizeFlags(currentUserContext);
}
}, 60 * 60 * 1000); // Every hour
With storage limits, prioritize saving your most critical flags:
// Identify critical flags that should always be saved
const CRITICAL_FLAGS = [
'payment-gateway',
'auth-method',
'critical-config'
];
// Save critical flags first
async function saveOfflinePriorities(context) {
const criticalValues = await flagdeck.getValues(CRITICAL_FLAGS, context);
await flagdeck.saveMultipleForOffline(criticalValues);
}
Track when offline values are being used:
flagdeck.use({
name: 'OfflineMonitoring',
afterEvaluation: (flagKey, result, context) => {
if (result.source === 'offline') {
// Log or track offline usage
analytics.track('Flag Offline Usage', {
flagKey,
value: result.value,
timestamp: Date.now()
});
}
return result;
}
});
When offline mode is enabled, flag evaluation results include a source
property that indicates where the value came from:
const result = await flagdeck.evaluateFlag('new-feature');
switch (result.source) {
case 'api':
// Value came directly from the API
console.log('Using latest value from API');
break;
case 'cache':
// Value came from in-memory cache
console.log('Using cached value');
break;
case 'offline':
// Value came from offline storage due to network error
console.log('Using offline stored value due to network error');
// You might want to show a subtle indicator that the app is working offline
showOfflineIndicator();
break;
case 'error':
// Error occurred and no offline value was available
console.log('Using default value due to error');
break;
}
If you're experiencing issues with offline mode:
Enable debugging for detailed logs:
const flagdeck = new Flagdeck({
apiKey: 'your-api-key',
enableOfflineMode: true,
debug: true
});
// Check if storage is available in browser
function isStorageAvailable() {
try {
const test = '__storage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
console.log('Storage available:', isStorageAvailable());
flagdeck.use({
name: 'OfflineDebugger',
afterEvaluation: (flagKey, result, context) => {
console.log(`Flag ${flagKey} evaluation:`, {
value: result.value,
source: result.source,
timestamp: new Date(result.timestamp).toISOString()
});
return result;
}
});
flushAnalytics()
before page unloads~/.flagdeck/
(home directory)Here's how you might integrate offline mode in a React application:
import React, { useState, useEffect } from 'react';
import { Flagdeck } from '@flagdeck/js';
// Create the client at the app level
const flagdeck = new Flagdeck({
apiKey: 'your-api-key',
enableOfflineMode: true,
debug: process.env.NODE_ENV !== 'production'
});
// Bootstrap offline flags at startup
useEffect(() => {
async function bootstrapFlags() {
try {
const criticalFlags = await flagdeck.getValues([
'new-feature',
'experiment-variant',
'api-config',
], userContext);
await flagdeck.saveMultipleForOffline(criticalFlags);
console.log('Critical flags bootstrapped for offline use');
} catch (error) {
console.warn('Failed to bootstrap flags:', error);
}
}
bootstrapFlags();
// Flush analytics and important flags on unmount
return () => {
flagdeck.flushAnalytics().catch(err => {
console.warn('Failed to flush analytics:', err);
});
};
}, []);
// Custom hook for feature flags with offline support
function useFeatureFlag(flagKey, defaultValue, context) {
const [value, setValue] = useState(defaultValue);
const [loading, setLoading] = useState(true);
const [source, setSource] = useState(null);
useEffect(() => {
let isMounted = true;
async function checkFlag() {
try {
setLoading(true);
const result = await flagdeck.evaluateFlag(flagKey, context);
if (isMounted) {
setValue(result.value);
setSource(result.source);
// Save successful evaluation for future offline use
if (result.source === 'api' || result.source === 'cache') {
flagdeck.saveForOffline(flagKey, result.value)
.catch(err => console.warn('Failed to save for offline:', err));
}
}
} catch (err) {
console.error(`Error evaluating flag ${flagKey}:`, err);
// Keep default value
} finally {
if (isMounted) {
setLoading(false);
}
}
}
checkFlag();
return () => { isMounted = false; };
}, [flagKey, JSON.stringify(context)]);
return { value, loading, source };
}
// Component using feature flag
function FeatureComponent({ user }) {
const context = {
userId: user.id,
attributes: { plan: user.plan, country: user.country }
};
const { value: isNewFeatureEnabled, loading, source } =
useFeatureFlag('new-feature', false, context);
// Show offline indicator if using offline storage
const isOffline = source === 'offline';
if (loading) return <div>Loading...</div>;
return (
<div>
{isOffline && <div className="offline-indicator">Using offline data</div>}
{isNewFeatureEnabled ? <NewFeatureComponent /> : <OldFeatureComponent />}
</div>
);
}
Offline mode in Flagdeck provides robust feature flag evaluation in any network condition. The implementation includes automatic storage management, flag expiration, and efficient pruning to ensure optimal performance.
Key takeaways:
With these strategies, your application can provide a consistent experience regardless of network conditions while ensuring efficient storage usage.
Start using Flagdeck today
Simple feature flag management for modern development teams.
Product
© 2025 Flagdeck. All rights reserved.