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.

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.

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:

# apps/web/.env
VITE_ACCOUNT_MODE=hybrid # personal-only | organizations-only | hybrid
ModePersonal accountsTeam accountsBehavior
personal-onlyYesNoB2C. Team routes, creation, and the account switcher are hidden.
organizations-onlyNo (as a workspace)YesB2B. Users land on a team; personal account is never the working surface. Equivalent to the old "teams only".
hybrid (default)YesYesB2B2C. 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

FlagEnvironment VariableDefaultDescription
Account ModeVITE_ACCOUNT_MODEhybridpersonal-only | organizations-only | hybrid (see above)
Theme ToggleVITE_ENABLE_THEME_TOGGLEtrueAllow users to switch themes
Account DeletionVITE_ENABLE_PERSONAL_ACCOUNT_DELETIONfalseUsers can delete their accounts (gated: not in organizations-only)
Team CreationVITE_ENABLE_TEAM_ACCOUNTS_CREATIONtrueUsers can create new teams (gated: not in personal-only)
Team DeletionVITE_ENABLE_TEAM_ACCOUNTS_DELETIONfalseUsers can delete their teams (gated: not in personal-only)
Personal BillingVITE_ENABLE_PERSONAL_ACCOUNT_BILLINGfalseBilling for personal accounts (gated: not in organizations-only)
Team BillingVITE_ENABLE_TEAM_ACCOUNTS_BILLINGfalseBilling for team accounts (gated: not in personal-only)
NotificationsVITE_ENABLE_NOTIFICATIONStrueIn-app notification system
Realtime NotificationsVITE_REALTIME_NOTIFICATIONSfalseLive notification updates
Version UpdaterVITE_ENABLE_VERSION_UPDATERfalseCheck for app updates
Language PriorityVITE_LANGUAGE_PRIORITYapplicationUser vs app language preference

Common Configurations

B2C SaaS (Personal Accounts Only)

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

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:

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:

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:

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 CaseThemeTeamsTeam CreationPersonal BillingTeam BillingDeletion
B2C Consumer AppYesNo-Yes-Yes
B2B Team SaaSOptionalYesYesNoYesOptional
Enterprise SaaSOptionalYesNoNoYesNo
Freemium PersonalYesNo-Yes-Yes
MarketplaceOptionalYesYesYesNoYes

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:

// 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

import featureFlagsConfig from '#/config/feature-flags.config.ts';
export default function SettingsPage() {
return (
<div>
{featureFlagsConfig.enableTeamAccounts && (
<TeamAccountsSection />
)}
{featureFlagsConfig.enableAccountDeletion && (
<DeleteAccountButton />
)}
</div>
);
}

In Client Components

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:

// 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.