Vercel Flags SDK
The Basestack Vercel Flags SDK adapter allows you to use Basestack Feature Flags with the Vercel Flags SDK, providing a seamless integration for Next.js applications. This adapter bridges Basestack's feature flag infrastructure with Vercel's type-safe flag system, giving you the best of both worlds.
With this adapter, you can leverage Vercel Flags' excellent developer experience including type safety, server-side rendering support, and a clean API while using Basestack as your feature flag backend. This is perfect if you're already familiar with Vercel Flags or want to use its patterns in your Next.js application.
Preview Status: This provider is currently in preview and still under active development. Basic feature flag functionality is supported, but advanced features like evaluation context and user identity targeting are still in the works. We recommend using this provider if you only need basic feature flag functionality.
Getting started
Examples
Install
Install both the Vercel Flags SDK and the Basestack adapter. The flags package provides the core Vercel Flags functionality, while @basestack/vercel-flags-sdk-adapter connects it to Basestack's API.
npm install flags @basestack/vercel-flags-sdk-adapterpnpm install flags @basestack/vercel-flags-sdk-adapteryarn add flags @basestack/vercel-flags-sdk-adapterbun add flags @basestack/vercel-flags-sdk-adapterEnvironment Variables
Configure your environment variables to connect to your Basestack Feature Flags project. The NEXT_PUBLIC_ prefix makes these variables available in both server and client components.
Make sure to add your .env file to .gitignore to keep your keys secure. Never commit sensitive credentials to version control.
# BASESTACK FEATURE FLAGS
NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY=""
NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY=""You can find your project and environment keys in your Basestack Feature Flags Dashboard.
Keep your project and environment keys secure. Never commit them to version control. Use environment variables or secure secret management systems.
Create the Adapter
Create a Basestack adapter instance that connects Vercel Flags to your Basestack project. The adapter handles API communication, caching, and value resolution.
Basic Boolean Flag Adapter:
import { flag } from "flags/next";
import { createBasestackAdapter } from "@basestack/vercel-flags-sdk-adapter";
const adapter = createBasestackAdapter<boolean>({
endpoint: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
cacheTtlMs: 15_000, // Cache flags for 15 seconds
requestTimeoutMs: 5_000, // 5 second timeout
resolveValue: (flag) => flag.enabled, // Return the enabled state for boolean flags
});
export const headerFlag = flag<boolean>({
key: "header",
adapter,
defaultValue: false,
description: "Shows the next generation header experience",
});The adapter configuration includes:
endpoint: Your Basestack API endpointprojectKey&environmentKey: Your Basestack credentialscacheTtlMs: How long to cache flag values (in milliseconds)requestTimeoutMs: Request timeout durationresolveValue: Function that extracts the flag value from Basestack's response
Use Flags in Your Application
Once configured, you can use flags in your Next.js server components, API routes, and server actions. Flags are fetched server-side, providing excellent performance and SEO benefits.
import { headerFlag } from "@/lib/flags";
export const dynamic = "force-dynamic";
export default async function HomePage() {
const showHeader = await headerFlag();
return (
<main className="page">
<section className="card muted">
<h1>
{showHeader ? "New header experience" : "Default header experience"}
</h1>
<p>Powered by Basestack + Vercel Flags.</p>
</section>
</main>
);
}The headerFlag() function returns a promise that resolves to the flag's value. Since this is a server component, the flag is evaluated on the server during rendering.
Advanced Usage
String/Variant Flags
For flags that return string values (like A/B test variants), create a string adapter with a custom resolveValue function:
import { flag } from "flags/next";
import { createBasestackAdapter } from "@basestack/vercel-flags-sdk-adapter";
const stringAdapter = createBasestackAdapter<string>({
endpoint: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
cacheTtlMs: 15_000,
requestTimeoutMs: 5_000,
resolveValue: (flag) => flag.payload?.variant as string, // Extract variant from payload
});
export const headerVariantFlag = flag<string>({
key: "header-variant",
adapter: stringAdapter,
defaultValue: "old",
description: "Header variant for A/B testing",
});Multiple Adapters
You can create multiple adapters for different flag types or configurations:
import { flag } from "flags/next";
import { createBasestackAdapter } from "@basestack/vercel-flags-sdk-adapter";
// Boolean adapter for simple on/off flags
const booleanAdapter = createBasestackAdapter<boolean>({
endpoint: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
cacheTtlMs: 15_000,
requestTimeoutMs: 5_000,
resolveValue: (flag) => flag.enabled,
});
// String adapter for variant flags
const variantAdapter = createBasestackAdapter<string>({
endpoint: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
cacheTtlMs: 15_000,
requestTimeoutMs: 5_000,
resolveValue: (flag) => flag.payload?.variant as string,
});
// Use boolean adapter
export const headerFlag = flag<boolean>({
key: "header",
adapter: booleanAdapter,
defaultValue: false,
});
// Use variant adapter
export const themeFlag = flag<string>({
key: "theme",
adapter: variantAdapter,
defaultValue: "light",
});API Routes
Use flags in Next.js API route handlers:
import { NextResponse } from "next/server";
import { headerFlag } from "@/lib/flags";
export async function GET() {
const isEnabled = await headerFlag();
if (isEnabled) {
return NextResponse.json({
message: "New feature enabled",
data: { /* new feature data */ }
});
}
return NextResponse.json({
message: "Legacy feature",
data: { /* legacy data */ }
});
}Server Actions
Flags work seamlessly with Next.js Server Actions:
"use server";
import { headerFlag } from "@/lib/flags";
export async function getFeatureData() {
const showNewFeature = await headerFlag();
if (showNewFeature) {
return { feature: "new", data: "..." };
}
return { feature: "legacy", data: "..." };
}Configuration Options
The createBasestackAdapter function accepts the following configuration:
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
endpoint | string | Yes | - | Basestack API endpoint URL |
projectKey | string | Yes | - | Your Basestack project key |
environmentKey | string | Yes | - | Your Basestack environment key |
cacheTtlMs | number | No | 15000 | Cache time-to-live in milliseconds |
requestTimeoutMs | number | No | 5000 | Request timeout in milliseconds |
resolveValue | (flag: Flag) => T | Yes | - | Function to extract the flag value from Basestack's response |
Best Practices
1. Organize Flags in a Single File
Keep all your flag definitions in a centralized location for easier management:
// src/lib/flags.ts
export const headerFlag = flag<boolean>({ /* ... */ });
export const footerFlag = flag<boolean>({ /* ... */ });
export const checkoutFlag = flag<string>({ /* ... */ });2. Use TypeScript Generics
Leverage TypeScript's type system for better type safety:
export const themeFlag = flag<"light" | "dark">({
key: "theme",
adapter: variantAdapter,
defaultValue: "light",
});3. Set Appropriate Cache TTLs
Balance freshness with performance:
// Short cache for frequently changing flags
const shortCacheAdapter = createBasestackAdapter({
// ...
cacheTtlMs: 5_000, // 5 seconds
});
// Longer cache for stable flags
const longCacheAdapter = createBasestackAdapter({
// ...
cacheTtlMs: 60_000, // 1 minute
});4. Handle Errors Gracefully
Always provide sensible default values:
export const criticalFlag = flag<boolean>({
key: "critical-feature",
adapter,
defaultValue: false, // Safe fallback if flag fetch fails
});5. Use Descriptive Flag Keys
Choose clear, consistent naming for your flags:
// Good
export const newCheckoutFlowFlag = flag<boolean>({ key: "new-checkout-flow", ... });
// Avoid
export const flag1 = flag<boolean>({ key: "f1", ... });Troubleshooting
Flags Not Updating
If flags aren't updating as expected:
- Check Cache TTL: Flags are cached based on
cacheTtlMs. Wait for the cache to expire or reduce the TTL - Verify Environment Variables: Ensure
NEXT_PUBLIC_prefixed variables are set correctly - Check Network: Verify your application can reach the Basestack API endpoint
- Restart Dev Server: After changing environment variables, restart your Next.js dev server
Type Errors
If you're getting TypeScript errors:
- Check Generic Types: Ensure the adapter and flag use matching types
- Verify resolveValue: The
resolveValuefunction must return the correct type - Import Types: Make sure you're importing types correctly from
flags/next
Flags Returning Default Values
If flags always return default values:
- Check Flag Key: Verify the flag key matches what's configured in Basestack
- Verify Credentials: Ensure project and environment keys are correct
- Check API Response: Inspect the network tab to see if the API is returning data
- Review resolveValue: Ensure
resolveValuecorrectly extracts the value from Basestack's response
Performance Issues
If you're experiencing performance problems:
- Increase Cache TTL: Reduce API calls by caching longer
- Reduce Request Timeout: Set a lower
requestTimeoutMsto fail fast - Preload Flags: Consider preloading critical flags at application startup