featurely-site-manager
Maintenance mode, feature flags, status banners, version management, and analytics.
featurely-site-manager
Installation
npm install featurely-site-managerRequires a public:read API key.
Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Required. API key with public:read permission |
projectId | string | — | Required. Your project ID |
apiUrl | string | — | Custom API endpoint |
environment | string | — | Hostname override or environment slug (useful server-side) |
pollInterval | number | 60000 | Config polling interval in ms |
userEmail | string | — | Current user's email (for maintenance whitelist checks) |
userId | string | — | Current user's ID |
customAttributes | Record<string, string | number | boolean> | — | Targeting attributes for feature flag rules |
bootstrapFlags | Record<string, boolean> | — | Pre-loaded flag values for SSR to avoid layout shift |
enableAnalytics | boolean | true | Enable automatic analytics tracking |
analyticsFlushInterval | number | 60000 | How often to flush analytics events (ms) |
autoCaptureClicks | boolean | false | Auto-track clicks on elements with data-featurely-click |
autoInjectBanners | boolean | true | Auto-inject status message banners into the page DOM |
enableHeatmaps | boolean | false | Enable click heatmap tracking with coordinates |
enableRageClickDetection | boolean | false | Detect rage clicks (multiple rapid clicks) |
enableScrollTracking | boolean | false | Track scroll depth per page |
enablePerformanceTracking | boolean | false | Track resource timing and long tasks |
heatmapSampleRate | number | 10 | Percentage of sessions to collect heatmap data from (0–100) |
appVersion | string | — | Current application version |
enableVersionCheck | boolean | false | Enable automatic version checking |
versionCheckInterval | number | 3600000 | Version check interval in ms |
platform | string | — | web, ios, android, electron, or tauri |
updateRules | VersionUpdateRules | — | Override how SemVer change types are classified |
debugMode | boolean | false | Enable the floating debug overlay panel |
debugSecret | string | — | Token for the production debug handshake (?ft_debug=<token>) |
bypassCheck | () => boolean | — | Custom function — return true to bypass maintenance mode |
onMaintenanceEnabled | (config: MaintenanceConfig) => void | — | Callback when maintenance mode activates |
onMaintenanceDisabled | () => void | — | Callback when maintenance mode deactivates |
onMessageReceived | (message: StatusMessage) => void | — | Callback when a new status message arrives |
onMessageDismissed | (messageId: string) => void | — | Callback when a message is dismissed |
onFeatureFlagsUpdated | (flags: FeatureFlag[]) => void | — | Callback when feature flags update |
onUpdateAvailable | (info: VersionCheckResponse) => void | — | Callback when a new version is available |
onUpdateRequired | (info: VersionCheckResponse) => void | — | Callback when an update is required |
onError | (error: Error) => void | — | Callback when the SDK encounters an error |
Basic usage
import SiteManager from 'featurely-site-manager';
const siteManager = new SiteManager({
apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
});
await siteManager.init();Maintenance mode
import SiteManager from 'featurely-site-manager';
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
onMaintenanceEnabled: () => {
// Redirect or show maintenance page
window.location.href = '/maintenance';
},
onMaintenanceDisabled: () => {
// Site is back up
window.location.reload();
},
});
await siteManager.init();
console.log(siteManager.isInMaintenanceMode()); // falseStatus messages
Status messages are automatically injected as banners when autoInjectBanners: true (the default). Each message has:
- 4 types:
info,warning,error,success - 2 positions:
top,bottom - 2 styles:
banner,toast - Optional start and end schedule
- Dismissible by users
To handle messages manually instead:
import SiteManager from 'featurely-site-manager';
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
autoInjectBanners: false,
onMessageReceived: (message) => {
console.log('New message:', message.title);
},
});
await siteManager.init();
const messages = siteManager.getActiveMessages();Feature flags
For a walkthrough of using Featurely feature flags on indie and small-team stacks, see Feature flags without LaunchDarkly.
import SiteManager from 'featurely-site-manager';
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
});
await siteManager.init();
// Simple boolean flag
if (siteManager.isFeatureEnabled('new_dashboard')) {
// Show new dashboard
}
// A/B variant
const variant = siteManager.getFeatureVariant('checkout_flow'); // 'control' | 'variant_a' | ...
// Local override (useful for testing) — pass null to remove the override
siteManager.overrideFlag('new_dashboard', true);
// URL param override (applies automatically): ?ft_new_dashboard=trueTargeting with custom attributes
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
customAttributes: {
plan: 'pro',
country: 'no',
beta: true,
},
});SSR / Bootstrap flags
Pre-load flag values on the server to avoid layout shift:
// Pass pre-fetched flags to avoid a loading state on the client
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
bootstrapFlags: {
new_dashboard: true,
checkout_v2: false,
},
});User identity
Call setUser() after login to associate analytics events and feature flag targeting with the current user:
siteManager.setUser('user@example.com', 'usr_123', { plan: 'pro' });Version management
import SiteManager from 'featurely-site-manager';
const siteManager = new SiteManager({
apiKey: 'ft_live_your_api_key_here',
projectId: 'your-project-id',
appVersion: '1.2.3',
enableVersionCheck: true,
updateRules: {
major: 'required', // major bump → force update
minor: 'available', // minor bump → optional
patch: 'recommended', // patch → recommended
},
onUpdateAvailable: (info) => {
console.log('Update available:', info.latestVersion?.version);
},
onUpdateRequired: async (info) => {
await siteManager.forceUpdateWeb();
},
});
await siteManager.init();updateRules fields:
major:"required"|"available"(default:"required")minor:"required"|"available"(default:"available")patch:"required"|"recommended"|"available"(default:"recommended")
Supported version formats: SemVer (1.2.3) and git hashes.
Analytics
The following events are tracked automatically:
| Event | Trigger |
|---|---|
session_start | SDK init |
page_view | Page navigation |
page_exit | Page unload |
outbound_link_click | Clicks on external links |
web_vital | Core Web Vitals |
feature_flag_evaluated | First flag evaluation per session |
user_login | When setUser() is called for the first time |
// Custom event
siteManager.trackEvent('signup_completed', { plan: 'pro' });
// Revenue event
siteManager.trackRevenue('purchase_completed', 49.99, 'USD', { productId: 'pro_plan' });
// Track 404s — call this in your 404 page component on mount
siteManager.track404();To track clicks automatically, add data-featurely-click attributes to elements and enable autoCaptureClicks: true in the config:
<button data-featurely-click="upgrade_button">Upgrade</button>Analytics are privacy-friendly: data is stored in localStorage, no cookies are used, and IP addresses are never stored.
Debug overlay
When debugMode is enabled, a floating panel appears with tabs for SDK state, feature flags, logs, network calls, events, and a test panel. In production, the overlay can be activated for a single page load via a URL parameter handshake:
?ft_debug=your_debug_secretSet debugSecret in your config to enable this.
All instance methods
| Method | Description |
|---|---|
init() | Initialize the SDK, fetch config, and start polling |
destroy() | Stop polling, flush analytics, and clean up |
setUser(email, userId?, customAttributes?) | Set user identity for targeting and analytics |
isInMaintenanceMode() | Returns whether maintenance mode is currently active |
getActiveMessages() | Returns active status messages |
isFeatureEnabled(key, default?) | Returns flag boolean for the current user |
getFeatureVariant(key) | Returns the A/B variant string for a flag |
overrideFlag(key, value | null) | Override a flag locally; pass null to remove |
getAllFeatureFlags() | Returns all flag objects from the loaded config |
getEnabledFeatures() | Returns keys of all flags enabled for the current user |
trackEvent(name, props?) | Track a custom analytics event |
trackRevenue(name, amount, currency, props?) | Track a revenue event |
track404(path?) | Track a page-not-found event |
checkVersion(version?) | Manually trigger a version check |
forceUpdateWeb() | Force a full page reload to apply an update |
getLastVersionCheck() | Returns the last version check result |
getActiveEnvironment() | Returns the matched environment config |
isErrorLoggingEnabled() | Returns whether error logging is on for the matched environment |
refresh() | Force re-fetch config from the API |
TypeScript exports
SiteManager (default), SiteManagerConfig, VersionUpdateRules, FeatureFlag, StatusMessage, MaintenanceConfig, VersionCheckResponse, SiteConfig, AnalyticsConfig, AppVersion