# Feature Flags Configuration in the Tanstack Start Supabase SaaS Kit

> Enable or disable team accounts, billing, notifications, and theme toggling in the Tanstack Start Supabase SaaS Kit using feature flags.

*Canonical: https://makerkit.dev/docs/tanstack-supabase/configuration/feature-flags-configuration*

---

The feature flags configuration at `apps/web/config/feature-flags.config.ts` controls which features are enabled in your application. Toggle team accounts, billing, notifications, and more using environment variables.

{% alert type="default" title="Feature Flags vs Configuration" %}
Feature flags control whether functionality is available to users. Use them to ship different product tiers, run A/B tests, or disable features during maintenance. Unlike configuration, feature flags are meant to change at runtime or between deployments.
{% /alert %}

{% alert type="warning" title="Defaults Note" %}
The "Default" column shows what the code uses if the environment variable is not set. The kit's `.env` file ships with different values to demonstrate features. Check your `.env` file for the actual starting values.
{% /alert %}

## Account Mode

Whether your app has personal accounts, team accounts, or both is controlled by a **single** variable — `VITE_ACCOUNT_MODE` — not by individual team flags:

```bash
# apps/web/.env
VITE_ACCOUNT_MODE=hybrid  # personal-only | organizations-only | hybrid
```

| Mode | Personal accounts | Team accounts | Behavior |
|------|-------------------|---------------|----------|
| `personal-only` | Yes | No | B2C. Team routes, creation, and the account switcher are hidden. |
| `organizations-only` | No (as a workspace) | Yes | B2B. Users land on a team; personal account is never the working surface. Equivalent to the old "teams only". |
| `hybrid` (default) | Yes | Yes | B2B2C. Users have a personal workspace and can create/join teams. |

`VITE_ACCOUNT_MODE` is the source of truth: the `enableTeamAccounts`, `enableTeamsOnly`, and `enableTeamCreation` flags are **derived** from it, and the billing/deletion toggles below are **gated** by it — a mode never exposes a surface it disables. It also drives context-aware navigation (routes tagged with a `context` of `personal` or `organization` only appear on the matching surface) and is enforced server-side when switching the active account.

## Available Feature Flags

| Flag | Environment Variable | Default | Description |
|------|---------------------|---------|-------------|
| Account Mode | `VITE_ACCOUNT_MODE` | `hybrid` | `personal-only` \| `organizations-only` \| `hybrid` (see above) |
| Theme Toggle | `VITE_ENABLE_THEME_TOGGLE` | `true` | Allow users to switch themes |
| Account Deletion | `VITE_ENABLE_PERSONAL_ACCOUNT_DELETION` | `false` | Users can delete their accounts (gated: not in `organizations-only`) |
| Team Creation | `VITE_ENABLE_TEAM_ACCOUNTS_CREATION` | `true` | Users can create new teams (gated: not in `personal-only`) |
| Team Deletion | `VITE_ENABLE_TEAM_ACCOUNTS_DELETION` | `false` | Users can delete their teams (gated: not in `personal-only`) |
| Personal Billing | `VITE_ENABLE_PERSONAL_ACCOUNT_BILLING` | `false` | Billing for personal accounts (gated: not in `organizations-only`) |
| Team Billing | `VITE_ENABLE_TEAM_ACCOUNTS_BILLING` | `false` | Billing for team accounts (gated: not in `personal-only`) |
| Notifications | `VITE_ENABLE_NOTIFICATIONS` | `true` | In-app notification system |
| Realtime Notifications | `VITE_REALTIME_NOTIFICATIONS` | `false` | Live notification updates |
| Version Updater | `VITE_ENABLE_VERSION_UPDATER` | `false` | Check for app updates |
| Language Priority | `VITE_LANGUAGE_PRIORITY` | `application` | User vs app language preference |

{% alert type="warning" title="Replaces VITE_ENABLE_TEAM_ACCOUNTS(_ONLY)" %}
`VITE_ENABLE_TEAM_ACCOUNTS` and `VITE_ENABLE_TEAM_ACCOUNTS_ONLY` are no longer read — set `VITE_ACCOUNT_MODE` instead (`false`/`true` → `personal-only`, teams-only → `organizations-only`, both → `hybrid`).
{% /alert %}

## Common Configurations

### B2C SaaS (Personal Accounts Only)

For consumer applications where each user has their own account and subscription:

```bash
VITE_ACCOUNT_MODE=personal-only
VITE_ENABLE_PERSONAL_ACCOUNT_BILLING=true
VITE_ENABLE_PERSONAL_ACCOUNT_DELETION=true
```

### B2B SaaS (Team Accounts Only)

For business applications where organizations subscribe and manage team members. Set `VITE_ACCOUNT_MODE=organizations-only` to skip personal accounts entirely:

```bash
VITE_ACCOUNT_MODE=organizations-only
VITE_ENABLE_TEAM_ACCOUNTS_BILLING=true
VITE_ENABLE_TEAM_ACCOUNTS_DELETION=true
```

When `VITE_ACCOUNT_MODE=organizations-only`:

- Users are automatically redirected away from personal account routes to their team workspace
- The personal account section in the sidebar/workspace switcher is hidden
- After sign-in, users land on their team dashboard instead of a personal home page
- If the user has no team yet, they are routed to the create-team flow

This is the recommended approach for B2B apps. It removes the personal account layer entirely so users only interact with team workspaces.

### Hybrid Model (Both Personal and Team)

For applications supporting both individual users and teams:

```bash
VITE_ACCOUNT_MODE=hybrid
VITE_ENABLE_PERSONAL_ACCOUNT_BILLING=true
VITE_ENABLE_TEAM_ACCOUNTS_BILLING=true
```

### Managed Onboarding (No Self-Service Team Creation)

For applications where you create teams on behalf of customers:

```bash
VITE_ACCOUNT_MODE=organizations-only
VITE_ENABLE_TEAM_ACCOUNTS_CREATION=false
VITE_ENABLE_TEAM_ACCOUNTS_BILLING=true
```

## Decision Matrix

Use this matrix to decide which flags to enable:

| Use Case | Theme | Teams | Team Creation | Personal Billing | Team Billing | Deletion |
|----------|-------|-------|---------------|------------------|--------------|----------|
| B2C Consumer App | Yes | No | - | Yes | - | Yes |
| B2B Team SaaS | Optional | Yes | Yes | No | Yes | Optional |
| Enterprise SaaS | Optional | Yes | No | No | Yes | No |
| Freemium Personal | Yes | No | - | Yes | - | Yes |
| Marketplace | Optional | Yes | Yes | Yes | No | Yes |

## How It Works

`VITE_ACCOUNT_MODE` is resolved in `apps/web/config/account-mode.config.ts`, which exposes `getModeFeatureFlags()`. The feature flags config derives the account-context flags from the mode and gates the granular toggles against it:

```typescript
// account-mode.config.ts — single source of truth
export function getModeFeatureFlags() {
  const mode = accountModeConfig.mode; // VITE_ACCOUNT_MODE, default 'hybrid'

  return {
    accountMode: mode,
    enableTeamAccounts: mode !== 'personal-only',
    enablePersonalAccount: mode !== 'organizations-only',
    enableTeamCreation: mode !== 'personal-only',
    enableTeamsOnly: mode === 'organizations-only',
  } as const;
}

// feature-flags.config.ts — derives + gates
const mode = getModeFeatureFlags();

const featuresFlagConfig = FeatureFlagsSchema.parse({
  accountMode: mode.accountMode,
  enableTeamAccounts: mode.enableTeamAccounts,
  enableTeamsOnly: mode.enableTeamsOnly,
  // granular toggles are AND-gated by the mode
  enablePersonalAccountBilling:
    mode.enablePersonalAccount &&
    getBoolean(import.meta.env.VITE_ENABLE_PERSONAL_ACCOUNT_BILLING, false),
  enableTeamAccountBilling:
    mode.enableTeamAccounts &&
    getBoolean(import.meta.env.VITE_ENABLE_TEAM_ACCOUNTS_BILLING, false),
  // ...theme, notifications, version updater read their own env vars
});
```

## Using Feature Flags in Code

### In Server Components

```typescript
import featureFlagsConfig from '#/config/feature-flags.config.ts';

export default function SettingsPage() {
  return (
    <div>
      {featureFlagsConfig.enableTeamAccounts && (
        <TeamAccountsSection />
      )}
      {featureFlagsConfig.enableAccountDeletion && (
        <DeleteAccountButton />
      )}
    </div>
  );
}
```

### In Client Components

```tsx
import featureFlagsConfig from '#/config/feature-flags.config.ts';

export function ThemeToggle() {
  if (!featureFlagsConfig.enableThemeToggle) {
    return null;
  }

  return <ThemeSwitch />;
}
```

### Conditional Navigation

The navigation configuration files already use feature flags:

```typescript
// From components/settings/settings-navigation.tsx
featureFlagsConfig.enablePersonalAccountBilling
  ? {
      label: 'common.routes.billing',
      path: pathsConfig.app.settingsBilling,
      Icon: <CreditCard className={iconClasses} />,
    }
  : undefined,
```

## Feature Flag Details

### Theme Toggle

Controls whether users can switch between light and dark themes. When disabled, the app uses `VITE_DEFAULT_THEME_MODE` exclusively.

### Account Deletion

Allows users to permanently delete their personal accounts. Disabled by default to prevent accidental data loss. Consider enabling for GDPR compliance.

### Team Accounts

Team functionality is controlled by `VITE_ACCOUNT_MODE`, not a dedicated flag. `personal-only` disables all team features (navigation, creation, switching); `organizations-only` and `hybrid` enable them. The derived `enableTeamAccounts` / `enableTeamsOnly` flags remain available in code for conditional rendering.

### Team Creation

Controls whether users can create new teams. Set `VITE_ENABLE_TEAM_ACCOUNTS_CREATION=false` for enterprise scenarios where you provision teams manually. Ignored in `personal-only` mode (teams are disabled entirely).

### Team Deletion

Allows team owners to delete their teams. Disabled by default to prevent accidental data loss.

### Personal vs Team Billing

Choose one based on your business model:

- **Personal billing**: Each user subscribes individually (B2C)
- **Team billing**: Organizations subscribe and add team members (B2B)

Enabling both is possible but uncommon. Most SaaS applications use one model.

### Notifications

Enables the in-app notification system. When combined with `realtimeNotifications`, notifications appear instantly via Supabase Realtime.

### Language Priority

Controls language selection behavior:

- `application`: Use the app's default locale
- `user`: Respect the user's browser language preference

### Version Updater

When enabled, the app checks for updates and notifies users. Useful for deployed applications that receive frequent updates.

## Common Pitfalls

1. **Enabling both billing modes**: While technically possible, enabling both personal and team billing may create confusing user experience (unless your business model is a hybrid of both). Choose one model.
2. **Disabling teams after launch**: If you've collected team data and then disable teams, users lose access. Plan your model before launch.
3. **Forgetting deletion flows**: If you enable deletion, ensure you also handle cascading data deletion and GDPR compliance.
4. **Realtime without base notifications**: `realtimeNotifications` requires `enableNotifications` to be true. The realtime flag adds live updates, not the notification system itself.

## Related Topics

- [Application Configuration](/docs/tanstack-supabase/configuration/application-configuration) - Core app settings
- [Environment Variables](/docs/tanstack-supabase/configuration/environment-variables) - Complete variable reference
- [Navigation Configuration](/docs/tanstack-supabase/configuration/personal-account-sidebar-configuration) - Sidebar customization
