JavaScript (Official SDK)
Seamless integration with Vanilla JavaScript streamlines the testing and development of features across production and various environments. The Basestack JavaScript SDK is a lightweight, framework-agnostic library that works in any JavaScript environment, browser, Node.js, or serverless functions.
This SDK provides a simple, promise-based API for fetching and managing feature flags with built-in caching, error handling, and TypeScript support. Whether you're building with Vue, Svelte, Angular, or plain JavaScript, this SDK integrates effortlessly into your workflow.
Why feature flags? They let you ship and test in production safely: use environments to hide or show work-in-progress features, roll out gradually, or turn a misbehaving feature off in real time without a full deploy. Learn more on the Feature Flags product page.
Getting started
Examples
Example apps in the basestack-flags-js repository use the local package via link:../.. run a production build at the repo root (bun run build or your package managerβs build) before trying them.
Install
Install the SDK using your preferred package manager. The SDK is lightweight and has no external dependencies beyond the Fetch API (available in all modern browsers and Node.js 18+).
npm install @basestack/flags-jspnpm install @basestack/flags-jsyarn add @basestack/flags-jsbun add @basestack/flags-jsAlternative: Script Tag
For quick prototyping or when you can't use a bundler, you can include the SDK via a script tag:
<script type="module">
import { FlagsSDK } from "https://unpkg.com/@basestack/flags-js";
// Use FlagsSDK here
</script>Environment Variables
Configure your environment variables to connect to your Basestack Feature Flags project. The variable names can be customized to match your project's conventions.
When it comes to environment variables, pay attention to the framework you're
using. For example, NEXT_PUBLIC_ is specific to Next.js, while in
Vite.js, it would be VITE_. Example
VITE_FEATURE_FLAGS_BASE_URL= or FEATURE_FLAGS_BASE_URL=
# BASESTACK FEATURE FLAGS
FEATURE_FLAGS_BASE_URL="https://flags-api.basestack.co/v1"
FEATURE_FLAGS_PROJECT_KEY=""
FEATURE_FLAGS_ENVIRONMENT_KEY=""
# Optional β project API token for server-side helpers such as submitCodeReferences() only
# FEATURE_FLAGS_API_KEY=""You can find your project and environment keys in your Basestack Feature Flags Dashboard.
Make sure to add your .env file to .gitignore to keep your keys secure. Never commit sensitive credentials to version control.
Create SDK Instance
Create a new instance of the FlagsSDK class with your configuration. The SDK instance manages all flag operations, caching, and API communication.
Basic Initialization
For most use cases, a simple configuration is sufficient:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
baseURL: process.env.FEATURE_FLAGS_BASE_URL,
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
});Advanced Configuration
For production applications, you may want to customize caching behavior and preload specific flags:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
baseURL: process.env.FEATURE_FLAGS_BASE_URL,
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
// Preload specific flags on initialization
preloadFlags: ["header", "footer", "checkout"],
// Customize cache settings
cache: {
enabled: true,
ttl: 10 * 60 * 1000, // 10 minutes (default: 5 minutes)
maxSize: 50, // Maximum cached flags (default: 100)
},
// Optional β project API token from project settings; required for submitCodeReferences() and other authenticated writes (use server-side or CI only)
// apiKey: process.env.FEATURE_FLAGS_API_KEY,
});Key configuration options:
baseURL: Feature Flags API base URL (optional; defaults tohttps://flags-api.basestack.co/v1). For self-hosted Basestack, point this at your instance (for examplehttps://your-domain.com/api/v1).projectKey: Your project identifier (required)environmentKey: Your environment identifier (required)apiKey: Project API token for authenticated write methods such assubmitCodeReferences()prefer server-side or CI; never expose in client bundlespreloadFlags: If set,init()preloads only these slugs; if omitted,init()preloads all flagscache: Cache configuration object (optional)fetchImpl: Customfetchimplementation (optional; useful for Node.js versions before 18 or test doubles)
Initialize the SDK
Call init() to preload flags into the internal cache. It is optional but recommended when you want flags ready before rendering or to warm the cache.
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
});
// Preload flags β if preloadFlags is set in config, only those load; if omitted, all flags load
await client.init();Behavior:
- If
preloadFlagsis set in config,init()loads only those slugs. - If
preloadFlagsis omitted,init()loads all flags for the project and environment.
When to use init():
- β You want flags (or a defined subset) loaded up front
- β You rely on caching for fewer round trips
When you can skip init():
- β
You only call
getFlag()/getAllFlags()on demand the first fetch still works; caching applies after data is loaded
Fetch Flags
Now you can fetch flags using the SDK methods. The SDK provides two main methods: getFlag() for a single flag and getAllFlags() for all flags.
Fetching a Single Flag
Use getFlag() to retrieve a specific flag by its slug:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: "your-project-key",
environmentKey: "your-environment-key",
});
async function checkFeatureFlag() {
try {
const headerFlag = await client.getFlag("header");
if (headerFlag.enabled) {
console.log("Header feature is enabled");
console.log("Payload:", headerFlag.payload);
// Use the flag to enable features
enableNewHeader(headerFlag.payload);
} else {
console.log("Header feature is disabled");
// Use default behavior
useDefaultHeader();
}
} catch (error) {
console.error("Failed to fetch flag:", error);
// Fallback to default behavior
useDefaultHeader();
}
}
// Call the function
await checkFeatureFlag();Fetching All Flags
Use getAllFlags() to retrieve all flags for your environment:
async function listAllFlags() {
try {
const { flags } = await client.getAllFlags();
console.log(`Found ${flags.length} flag(s)`);
flags.forEach((flag) => {
console.log(`Flag: ${flag.slug}`);
console.log(`Enabled: ${flag.enabled}`);
console.log(`Description: ${flag.description}`);
console.log(`Payload:`, flag.payload);
});
} catch (error) {
console.error("Failed to fetch flags:", error);
}
}
await listAllFlags();Flag Object Structure
Each flag object contains:
slug: Unique identifier for the flagenabled: Boolean indicating if the flag is enabledpayload: Optional data associated with the flagdescription: Human-readable descriptioncreatedAt: Date when the flag was createdupdatedAt: Date when the flag was last updatedexpiredAt: Optional expiration date
Common Usage Patterns
Conditional Feature Rendering
Use flags to conditionally render features in your application:
async function renderHeader() {
try {
const headerFlag = await client.getFlag("new-header-design");
if (headerFlag.enabled) {
renderNewHeader(headerFlag.payload);
} else {
renderDefaultHeader();
}
} catch (error) {
console.error("Error loading header flag:", error);
// Always fallback to default
renderDefaultHeader();
}
}Cache Management
The SDK includes built-in caching to reduce API calls. Manage the cache as needed:
// Clear entire cache
client.clearCache();
// Clear cache for a specific flag
client.clearFlagCache("header");
// Flags will be fetched fresh on next request
const freshFlag = await client.getFlag("header");Preview flags and feedback
getPreviewFlags()
Returns preview flags with rendered HTML content (Lexical-based previews in the dashboard).
const previewResponse = await client.getPreviewFlags();
previewResponse.flags.forEach((previewFlag) => {
console.log(previewFlag.slug);
console.log(previewFlag.previewName);
console.log(previewFlag.previewContent);
});submitPreviewFeedback(payload)
Submit qualitative and quantitative feedback for a preview flag.
const feedback = await client.submitPreviewFeedback({
flagKey: "landing-preview",
mood: "HAPPY",
rating: 4,
description: "The preview looks good and reads clearly.",
metadata: {
page: "landing",
source: "marketing-site",
},
});
console.log(feedback.success);
console.log(feedback.feedbackId);Validation:
flagKeyβ requiredmoodβ required (see exported typePreviewFeedbackMood)ratingβ integer from1to5descriptionβ optional, max2000charactersmetadataβ optional; when provided, must be a plain object
Code references
submitCodeReferences(payload)
Upload or replace code references for the given branch and flag slugs (same contract as POST /flags/code-refs). Requires apiKey in the SDK config use from CI, static analysis, or server-side code; do not ship the API key to browsers.
const authenticatedClient = new FlagsSDK({
baseURL: "https://flags-api.basestack.co/v1",
projectKey: "your-project-key",
environmentKey: "your-environment-key",
apiKey: "your-project-api-token",
});
const result = await authenticatedClient.submitCodeReferences({
branch: "main",
projectName: "acme-web",
repositoryUrl: "https://github.com/acme/web",
references: {
"new-checkout": [
{
filePath: "src/features/checkout.tsx",
lineNumber: 42,
lineContent: "if (flags.newCheckout) {",
},
],
},
});
console.log(result.success);
console.log(result.message);Validation:
branchβ required, max255charactersprojectNameβ required, max150charactersrepositoryUrlβ optional, max255characters when providedreferencesβ object keyed by flag slug (each slug max150characters)- Each row:
filePath(max2000),lineNumber(positive integer),lineContent(max4000)
Advanced Usage
Custom Fetch Implementation
For environments without native Fetch API (Node.js < 18), provide a custom fetch implementation:
import fetch from "node-fetch";
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: "your-project-key",
environmentKey: "your-environment-key",
fetchImpl: fetch, // Use node-fetch or other implementation
});Error Handling Best Practices
Always handle errors gracefully:
async function getFlagSafely(slug, defaultValue = false) {
try {
const flag = await client.getFlag(slug);
return flag.enabled;
} catch (error) {
console.error(`Failed to fetch flag "${slug}":`, error);
// Return safe default value
return defaultValue;
}
}
// Usage with fallback
const isEnabled = await getFlagSafely("new-feature", false);TypeScript Usage
The SDK is built with TypeScript and provides full type safety:
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";
const config: SDKConfig = {
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY!,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY!,
};
const client = new FlagsSDK(config);
// Type-safe flag fetching
async function getTypedFlag(): Promise<Flag> {
return await client.getFlag("header");
}
// Type-safe payload
interface HeaderPayload {
variant: "light" | "dark";
showLogo: boolean;
}
async function getHeaderConfig() {
const flag = await client.getFlag("header");
const payload = flag.payload as HeaderPayload;
return payload;
}Best Practices
1. Initialize Early
Initialize the SDK as early as possible in your application lifecycle:
// In your app initialization
const client = new FlagsSDK(config);
await client.init(); // Preload flags before rendering2. Use Caching
Enable caching to reduce API calls and improve performance:
const client = new FlagsSDK({
// ... other config
cache: {
enabled: true,
ttl: 5 * 60 * 1000, // 5 minutes
},
});3. Handle Errors Gracefully
Always provide fallback behavior when flags fail to load:
async function getFeatureState(flagSlug, fallback = false) {
try {
const flag = await client.getFlag(flagSlug);
return flag.enabled;
} catch (error) {
console.error(`Flag "${flagSlug}" failed:`, error);
return fallback;
}
}4. Preload Critical Flags
Preload flags that are needed immediately:
const client = new FlagsSDK({
// ... other config
preloadFlags: ["header", "footer", "checkout"],
});5. Use Environment-Specific Configs
Use different configurations for different environments:
const isProduction = process.env.NODE_ENV === "production";
const client = new FlagsSDK({
baseURL: isProduction
? "https://flags-api.basestack.co/v1"
: "https://flags-dev.basestack.co/v1",
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: isProduction
? process.env.PROD_ENVIRONMENT_KEY
: process.env.DEV_ENVIRONMENT_KEY,
});Troubleshooting
Flags Not Loading
If flags aren't loading:
- Check Configuration: Verify your
projectKeyandenvironmentKeyare correct - Check Network: Ensure your application can reach the Basestack API endpoint
- Check Console: Look for error messages in the browser console or server logs
- Verify Environment Variables: Ensure environment variables are set correctly
Cache Issues
If flags aren't updating:
- Clear Cache: Use
client.clearCache()to force fresh fetches - Check TTL: Verify your cache TTL isn't too long
- Manual Refresh: Fetch flags manually to bypass cache
TypeScript Errors
If you're getting TypeScript errors:
- Install Types: Types are included with the package, no additional installation needed
- Check Imports: Use
import typefor type-only imports - Type Assertions: Use type assertions for payloads when needed
Fetch API Not Available
If Fetch API isn't available (Node.js < 18):
- Install node-fetch:
npm install node-fetch - Provide Custom Fetch: Pass
fetchImploption to SDK config - Use Polyfill: Use a Fetch polyfill for older environments
TypeScript Support
The SDK is built with TypeScript and provides full type definitions. All types are exported for your use:
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";
const config: SDKConfig = {
projectKey: "your-key",
environmentKey: "your-env-key",
};
const client = new FlagsSDK(config);
const flag: Flag = await client.getFlag("header");Exported types
The package exports these public types (use import type { β¦ } where you only need types):
CacheConfig,SDKConfigFlag,PreviewFlag,PreviewFlagsResponsePreviewFeedbackMood,SubmitPreviewFeedbackPayload,SubmitPreviewFeedbackResponseCodeReferenceRow,SubmitCodeReferencesPayload,SubmitCodeReferencesResponse
Type Definitions
The SDK ships TypeScript definitions. Core config and flag shapes include:
/** Configuration for caching mechanism */
export interface CacheConfig {
/** Enable or disable caching (default: true) */
enabled?: boolean;
/** Time-to-Live for cache entries in milliseconds (default: 5 minutes) */
ttl?: number;
/** Maximum number of cache entries (default: 100) */
maxSize?: number;
}
/** SDK Configuration Options */
export interface SDKConfig {
/** Base URL for the Feature Flags API (optional; defaults to `https://flags-api.basestack.co/v1`) */
baseURL?: string;
/** Project identification key (required) */
projectKey: string;
/** Environment identification key (required) */
environmentKey: string;
/** Project API token for authenticated write endpoints such as `submitCodeReferences()` (optional; server-side only) */
apiKey?: string;
/** Caching configuration options */
cache?: CacheConfig;
/** Custom fetch implementation (optional) */
fetchImpl?: typeof fetch;
/** If set, `init()` preloads only these slugs; if omitted, `init()` preloads all flags */
preloadFlags?: string[];
}
/** Feature flag data structure */
export interface Flag {
slug: string;
enabled: boolean;
payload?: unknown;
expiredAt?: Date | null;
createdAt: Date;
updatedAt: Date;
description: string;
error?: boolean;
}SDK Reference
Initialization options
| Property | Type | Description | Required | Default value |
|---|---|---|---|---|
baseURL | string | Feature Flags API base URL. | false | https://flags-api.basestack.co/v1 |
projectKey | string | Project identifier. | true | β |
environmentKey | string | Environment identifier. | true | β |
apiKey | string | Project API token for write methods such as submitCodeReferences(). Prefer server-side usage. | false | β |
preloadFlags | string[] | If set, init() loads only these slugs; if omitted, init() loads all flags. | false | β |
cache.enabled | boolean | Enables or disables caching of flags. | false | true |
cache.ttl | number | Time-to-live for cached flags in milliseconds. | false | 5 * 60 * 1000 |
cache.maxSize | number | Maximum number of flags to store in the cache. | false | 100 |
fetchImpl | function | Custom fetch implementation (for Node.js versions before 18). | false | global.fetch |
Available methods
| Method | Description | Returns |
|---|---|---|
init() | Preloads flags per preloadFlags rules (or all flags if preloadFlags omitted). | Promise<void> |
getFlag(slug) | Fetches a single flag by slug. | Promise<Flag> |
getAllFlags() | Fetches all flags for the project and environment. | Promise<{ flags: Flag[] }> |
getPreviewFlags() | Fetches preview flags with rendered HTML content. | Promise<PreviewFlagsResponse> |
submitPreviewFeedback(payload) | Submits feedback for a preview flag. | Promise<SubmitPreviewFeedbackResponse> |
submitCodeReferences(payload) | Uploads code references (requires apiKey). | Promise<SubmitCodeReferencesResponse> |
clearCache() | Clears the entire in-memory cache. | void |
clearFlagCache(slug) | Clears cached entries for one slug. | void |
Method details
init()
Preloads flags into the internal cache. If preloadFlags is set, only those slugs load; if preloadFlags is omitted, all flags load.
await client.init();getFlag(slug: string)
Fetches a single flag by its slug. Returns a Promise that resolves to a Flag object.
const flag = await client.getFlag("header");
console.log(flag.enabled); // true or false
console.log(flag.payload); // Optional payload datagetAllFlags()
Fetches all flags for the current environment. Returns a Promise that resolves to an object with a flags array.
const { flags } = await client.getAllFlags();
flags.forEach(flag => {
console.log(`${flag.slug}: ${flag.enabled}`);
});clearCache()
Clears all cached flags. Useful when you need to force fresh fetches.
client.clearCache();clearFlagCache(slug: string)
Clears the cache for a specific flag.
client.clearFlagCache("header");getPreviewFlags()
Returns preview flags with previewContent and related fields. See Preview flags and feedback.
submitPreviewFeedback(payload)
Submits preview feedback. See Preview flags and feedback for fields and validation.
submitCodeReferences(payload)
Uploads code references for the configured branch; requires apiKey in the SDK config. See Code references for the payload and validation rules.