Feature Flags
Control feature availability dynamically using environment-based feature flags
Feature flags allow you to enable or disable features based on environment configuration. This is perfect for controlling OAuth providers, beta features, or region-specific functionality.
Prerequisites
- Basic understanding of environment variables
- Familiarity with TypeScript
- Understanding of React Server Components (for server-side checks)
What Are Feature Flags?
Feature flags (also called feature toggles) allow you to:
- Dynamic Feature Control: Enable/disable features without code changes
- Environment-Based Configuration: Different features in dev vs production
- Gradual Rollout: Test features with a subset of users
- Quick Rollback: Disable problematic features instantly
Current Feature Flags
OAuth Provider Flags
The starter kit includes feature flags for OAuth providers. Providers are automatically enabled when their credentials are configured.
1export function getFeatures() {2 return {3 oauth: {4 github: {5 enabled: !!(6 process.env.AUTH_GITHUB_ID && process.env.AUTH_GITHUB_SECRET7 ),8 },9 google: {10 enabled: !!(11 process.env.AUTH_GOOGLE_ID && process.env.AUTH_GOOGLE_SECRET12 ),13 },14 },15 };16}This function checks if OAuth credentials exist. If credentials are missing, the provider is automatically disabled.
How It Works
- Environment Check: The
getFeatures()function reads environment variables - Dynamic Config: Auth.js providers are configured based on feature flags
- UI Rendering: Login buttons only show for enabled providers
- Runtime Safety: Attempting to use disabled providers returns an error
Using Feature Flags
Check Feature Availability
Import and use the feature detection function:
1import { getFeatures } from "@/lib/config/features";2 3export default function ExamplePage() {4 const features = getFeatures();5 6 return (7 <div>8 {features.oauth.github.enabled && (9 <button>Sign in with GitHub</button>10 )}11 12 {features.oauth.google.enabled && (13 <button>Sign in with Google</button>14 )}15 </div>16 );17}Enable/Disable OAuth Providers
Control which OAuth providers are available by setting their environment variables:
1# Enable GitHub OAuth2AUTH_GITHUB_ID="your-github-client-id"3AUTH_GITHUB_SECRET="your-github-secret"4 5# Enable Google OAuth6AUTH_GOOGLE_ID="your-google-client-id"7AUTH_GOOGLE_SECRET="your-google-secret"To disable a provider, simply remove or comment out its credentials:
1# Disable GitHub OAuth (remove or comment out)2# AUTH_GITHUB_ID="..."3# AUTH_GITHUB_SECRET="..."Automatic Disabling
false. Just remove the credentials and the feature is automatically disabled.Auth.js Provider Configuration
The Auth.js configuration dynamically includes providers based on feature flags:
1import { getFeatures } from "@/lib/config/features";2import GitHub from "next-auth/providers/github";3import Google from "next-auth/providers/google";4 5const features = getFeatures();6 7export const authOptions = {8 providers: [9 // Conditionally include GitHub10 ...(features.oauth.github.enabled11 ? [12 GitHub({13 clientId: process.env.AUTH_GITHUB_ID!,14 clientSecret: process.env.AUTH_GITHUB_SECRET!,15 }),16 ]17 : []),18 19 // Conditionally include Google20 ...(features.oauth.google.enabled21 ? [22 Google({23 clientId: process.env.AUTH_GOOGLE_ID!,24 clientSecret: process.env.AUTH_GOOGLE_SECRET!,25 }),26 ]27 : []),28 ],29 // ... other config30};Adding Custom Feature Flags
Extend the Features Object
Add your own feature flags to src/lib/config/features.ts:
1export function getFeatures() {2 return {3 oauth: {4 github: {5 enabled: !!(6 process.env.AUTH_GITHUB_ID && process.env.AUTH_GITHUB_SECRET7 ),8 },9 google: {10 enabled: !!(11 process.env.AUTH_GOOGLE_ID && process.env.AUTH_GOOGLE_SECRET12 ),13 },14 },15 16 // Add your custom features here17 betaFeatures: {18 aiAssistant: {19 enabled: process.env.FEATURE_AI_ASSISTANT === "true",20 },21 advancedAnalytics: {22 enabled: process.env.FEATURE_ANALYTICS === "true",23 },24 },25 26 // Feature with custom logic27 payments: {28 stripe: {29 enabled: !!process.env.STRIPE_SECRET_KEY,30 },31 lemonSqueezy: {32 enabled: !!process.env.LEMONSQUEEZY_API_KEY,33 },34 },35 };36}Add Environment Variables
Configure your new feature flags in .env.local:
1# Beta Features2FEATURE_AI_ASSISTANT="true"3FEATURE_ANALYTICS="false"Use in Your Code
Check the flag before rendering or executing feature code:
1import { getFeatures } from "@/lib/config/features";2 3export default function DashboardPage() {4 const features = getFeatures();5 6 return (7 <div>8 <h1>Dashboard</h1>9 10 {features.betaFeatures.aiAssistant.enabled && (11 <div className="ai-assistant">12 <h2>AI Assistant</h2>13 {/* AI Assistant UI */}14 </div>15 )}16 17 {features.betaFeatures.advancedAnalytics.enabled && (18 <div className="analytics">19 <h2>Advanced Analytics</h2>20 {/* Analytics Dashboard */}21 </div>22 )}23 </div>24 );25}Advanced Patterns
User-Based Feature Flags
Enable features for specific users or plans:
1export function getUserFeatures(user: User) {2 const baseFeatures = getFeatures();3 4 return {5 ...baseFeatures,6 premium: {7 advancedReports: {8 enabled: user.subscription?.planId === "pro" ||9 user.subscription?.planId === "enterprise",10 },11 prioritySupport: {12 enabled: user.subscription?.planId === "enterprise",13 },14 },15 };16}Environment-Based Flags
Different features in development vs production:
1export function getFeatures() {2 const isDev = process.env.NODE_ENV === "development";3 const isProd = process.env.NODE_ENV === "production";4 5 return {6 // ... other features7 8 debugging: {9 enabled: isDev, // Only in development10 },11 12 productionOnlyFeature: {13 enabled: isProd, // Only in production14 },15 };16}API Route Protection
Use feature flags to protect API routes:
1import { getFeatures } from "@/lib/config/features";2import { NextResponse } from "next/server";3 4export async function GET() {5 const features = getFeatures();6 7 if (!features.betaFeatures.aiAssistant.enabled) {8 return NextResponse.json(9 { error: "Feature not available" },10 { status: 403 }11 );12 }13 14 // Feature is enabled, proceed15 return NextResponse.json({ data: "Beta feature data" });16}Best Practices
1. Centralize Feature Detection
Keep all feature flag logic in src/lib/config/features.ts. Don't scatter flag checks throughout your codebase.
2. Use Environment Variables
Store flag state in .env.local (development) and in your hosting platform's environment settings (production).
3. Clean Up Old Flags
When a feature becomes permanent, remove the flag and the conditional logic. Don't accumulate stale flags.
4. Document Your Flags
Add comments explaining what each flag controls:
1betaFeatures: {2 // Enables AI-powered assistant in dashboard3 // Remove after Q1 2026 launch4 aiAssistant: {5 enabled: process.env.FEATURE_AI_ASSISTANT === "true",6 },7},5. Test Both States
Always test your application with the feature both enabled and disabled to ensure no errors occur.
Troubleshooting
Feature Not Enabling
- Verify environment variable is set correctly in
.env.local - Restart dev server after changing environment variables
- Check for typos in variable names
- Ensure boolean flags use exact string
"true"(nottruewithout quotes)
OAuth Provider Not Appearing
- Verify both
AUTH_*_IDandAUTH_*_SECRETare set - Check
getFeatures()returnsenabled: truefor the provider - Ensure Auth.js configuration includes the provider conditionally
Flag Changes Not Reflecting
- Server-side flags require server restart to take effect
- Client-side flags may need page refresh
- In production, redeploy after changing environment variables
Related Guides
Adding OAuth Providers
Add new OAuth providers or configure existing ones for user authentication
Environment Variables
Complete guide to configuring environment variables for local development and production
Production Deployment
Deploy your SaaS application to production with comprehensive setup and documentation removal instructions
Next Steps
Now that you understand feature flags, you might want to:
- Learn about Adding OAuth Providers to add more login options
- Explore Environment Variables for complete configuration guide
- Review Production Deployment for managing flags in production