Feature Flags
Enable and configure Account UI features
Feature Flags
Control which features are available in your Account UI deployment using feature flags. All flags are boolean environment variables and all of them carry the VITE_ prefix — Vite only exposes VITE_* vars to client code, so an unprefixed ENABLE_PASSKEY=true is silently ignored.
Where these come from
Resolution order for every flag is: worker-injected window.__QUICKBACK_RUNTIME blob → baked VITE_* env → built-in default. When a Quickback worker serves the Account UI it injects your quickback.config.ts account.auth settings into the blob, so you normally set these in the config rather than in a .env file. See With Quickback.
Reference
appConfig.features
These ten flags populate appConfig.features and are the ones isFeatureEnabled() accepts.
| Variable | features key | Default |
|---|---|---|
VITE_ENABLE_ORGANIZATIONS | organizations | false |
VITE_ENABLE_ORG_TEAMS | orgTeams | false |
VITE_ENABLE_ADMIN | admin | true |
VITE_ENABLE_FILE_UPLOADS | fileUploads | false |
VITE_ENABLE_PASSKEY | passkey | false |
VITE_ENABLE_EMAIL_OTP | emailOTP | false |
VITE_ENABLE_PASSWORD | password | true |
VITE_ENABLE_GOOGLE_OAUTH | googleOAuth | false |
VITE_ENABLE_SUBSCRIPTIONS | subscriptions | false |
VITE_ENABLE_REALTIME | realtime | false |
appConfig.auth
Three further flags configure the signup and email flows. They live on appConfig.auth, not on appConfig.features — isFeatureEnabled() does not accept them.
| Variable | auth key | Default |
|---|---|---|
VITE_ENABLE_SIGNUP | enableSignup | true |
VITE_ENABLE_EMAIL_VERIFICATION | requireEmailVerification | true |
VITE_DISABLE_EMAIL_STATUS_CHECK | disableEmailStatusCheck | false |
Password is on, passkey and OTP are off
The out-of-the-box posture is email + password only. Passkey, email OTP, organizations, and Google OAuth all default to false — you opt in. Admin defaults to true.
These are the SPA's built-in defaults — the compiler overrides most of them
The defaults above are what the Account SPA falls back to when nothing else sets a flag. In a compiled Quickback project the compiler derives the flags from your Better Auth config and account.auth shorthand, and only emits a VITE_* value when it has one — an unset flag emits nothing and the built-in default applies.
Two derivations are worth knowing because they do not match the built-in default:
organizationsresolves totrueunless you setaccount.auth.organizations: false, so a compiled project has organizations on even though the SPA default isfalse.adminis emitted only when you setaccount.auth.admin. Leave it unset and the compiler strips the/adminroute files from the bundle while the SPA's built-inadmin: truedefault still reports the feature as enabled. Setaccount.auth.adminexplicitly — in either direction — rather than relying on the default.
Authentication Features
User Signup
VITE_ENABLE_SIGNUP=true # default: trueControls whether new users can register.
When enabled:
- Shows "Sign Up" link on login page
/signuproute is accessible- New users can create accounts
When disabled:
- Signup route returns 404
- Only existing users can log in
- Useful for invite-only applications
Email Verification
VITE_ENABLE_EMAIL_VERIFICATION=true # default: trueControls the email verification flow.
When enabled:
- Users must verify email before full access
- Verification email sent on signup
- "Resend verification email" option available
- Unverified users see verification prompt
When disabled:
- Email addresses are trusted without verification
- Users have immediate access after signup
Security Consideration
Disabling email verification can allow fake email addresses. Only disable if you have another verification mechanism.
Email Deliverability Check
VITE_DISABLE_EMAIL_STATUS_CHECK=false # default: falseNote the inverted sense — this flag disables a check that is on by default.
When false (checking enabled):
- System validates email addresses are deliverable
- Rejects disposable/temporary email providers
- Prevents typos in domain names
When true (checking disabled):
- Accepts all email formats
- Useful for development/testing
- Allows
@test.com,@localhost, etc.
Password Authentication
VITE_ENABLE_PASSWORD=true # default: trueWhen enabled:
- Email + password fields on login page
- Password field on signup page
- Traditional username/password authentication
When disabled:
- No password fields shown
- Users authenticate via email OTP or passkey
Passkey Login
VITE_ENABLE_PASSKEY=false # default: falseControls passkey LOGIN only (not signup). When enabled, users can authenticate using passkeys.
When enabled:
- Users can register passkeys (fingerprint, Face ID, hardware keys)
- Passwordless login option
- "Manage Passkeys" page available
When disabled:
- No passkey registration
- No passkey login option
Requirements:
- HTTPS (passkeys require secure context)
- Modern browser with WebAuthn support
Email OTP
VITE_ENABLE_EMAIL_OTP=false # default: falseWhen enabled:
- Users can receive one-time passwords via email
- Alternative to password login
/email-otproute available
When disabled:
- No email OTP option
- Password or passkey required
Google OAuth
VITE_ENABLE_GOOGLE_OAUTH=false # default: falseWhen enabled:
- "Continue with Google" button on the login and signup pages
- Google row in the profile page's Connected Accounts card
When disabled:
- No social sign-in button, no Connected Accounts row
Requirements:
- Google OAuth credentials configured on the Better Auth side
Account Management Features
File Uploads
VITE_ENABLE_FILE_UPLOADS=false # default: falseWhen enabled:
- Avatar/profile picture upload
- Image cropping and editing
- File upload to R2/S3
When disabled:
- No file upload functionality
- Users can only use default avatars
Requirements:
- R2 bucket or S3 configured
- Upload endpoints in your API
Subscriptions
VITE_ENABLE_SUBSCRIPTIONS=false # default: falseWhen enabled:
- Subscription card on the organization page
- Subscription management in the admin dashboard
When disabled:
- No billing surface in the Account UI
Realtime
VITE_ENABLE_REALTIME=false # default: falseWhen enabled:
- The Account SPA opens a realtime websocket connection and reacts to server broadcasts (including the best-effort
auth:token-invalidatedevent described in JWT Optimization)
When disabled:
- No websocket connection
Organization Features
Organizations (Multi-Tenancy)
VITE_ENABLE_ORGANIZATIONS=false # default: falseWhen enabled:
- Users can create organizations
- Organization management pages
- Member invitations and roles
/organizations/*routes
When disabled:
- Single-user mode only
- No organization features
- Simpler user experience
Includes:
- Organization creation and deletion
- Member management (owner, admin, member roles)
- Invitation system
- Organization settings
Teams
VITE_ENABLE_ORG_TEAMS=false # default: falseSub-groups within an organization. Auto-set to true by the compiler when your Better Auth config has organization({ teams: { enabled: true } }). When false, the Teams tab is hidden and the auth client does not expose team APIs.
Admin Features
Admin Panel
VITE_ENABLE_ADMIN=true # default: trueWhen enabled:
/adminroute accessible to admin users- User management dashboard
- Subscription management
- Admin-only features:
- Create users manually
- Ban/unban users
- Reset user passwords
- View all sessions
- Manage subscriptions
When disabled:
- No admin panel
- Admin must use database directly
Requirements:
- User must have admin role in database
Feature Combinations
Minimal Configuration (Password-Only)
VITE_ENABLE_PASSWORD=true
VITE_ENABLE_PASSKEY=false
VITE_ENABLE_EMAIL_OTP=false
VITE_ENABLE_ORGANIZATIONS=false
VITE_ENABLE_ADMIN=falseSimple email/password authentication for apps that do not need advanced auth methods. Only VITE_ENABLE_ADMIN differs from the built-in defaults here.
Maximum Security
VITE_ENABLE_PASSKEY=true
VITE_ENABLE_EMAIL_OTP=true
VITE_ENABLE_PASSWORD=false
VITE_ENABLE_EMAIL_VERIFICATION=truePasswordless authentication with email verification and deliverability checks.
Multi-Tenant SaaS
VITE_ENABLE_PASSKEY=true
VITE_ENABLE_EMAIL_OTP=true
VITE_ENABLE_ORGANIZATIONS=true
VITE_ENABLE_ORG_TEAMS=true
VITE_ENABLE_ADMIN=trueFull-featured SaaS with organizations, teams, and admin panel.
Invite-Only Platform
VITE_ENABLE_SIGNUP=false
VITE_ENABLE_PASSKEY=true
VITE_ENABLE_ORGANIZATIONS=true
VITE_ENABLE_ADMIN=trueNo public signup — users must be created by admin or invited to organizations.
Feature Detection
Check if a feature is enabled in your code:
import { isFeatureEnabled } from '@/config/app';
if (isFeatureEnabled('organizations')) {
// Show organizations menu
}
if (isFeatureEnabled('passkey')) {
// Offer passkey setup
}isFeatureEnabled() accepts only the ten appConfig.features keys listed above. The three appConfig.auth flags are read from appConfig.auth directly:
import { appConfig } from '@/config/app';
if (appConfig.auth.enableSignup) {
// Show the sign-up link
}Dynamic Feature Configuration
Override features at runtime:
import { setAppConfig } from '@/config/app';
setAppConfig({
features: {
organizations: false, // Hide organization-management UI
passkey: true, // Enable passkey
},
});setAppConfig wins over the baked env
setAppConfig() merges its overrides into the already-resolved config, so it takes precedence over both the runtime blob and the baked VITE_* values. Use it for progressive feature rollouts or A/B testing. It cannot resurrect anything the build dropped: when organizations, passkey, or admin is off at compile time the compiler removes those route files before Vite runs, so re-enabling them at runtime shows a menu entry pointing at a route that is not in the bundle.
Testing Features
For local development, create .env.local:
# Test with all features enabled
VITE_ENABLE_SIGNUP=true
VITE_ENABLE_EMAIL_VERIFICATION=true
VITE_ENABLE_PASSKEY=true
VITE_ENABLE_EMAIL_OTP=true
VITE_ENABLE_PASSWORD=true
VITE_ENABLE_ORGANIZATIONS=true
VITE_ENABLE_ADMIN=true
VITE_ENABLE_FILE_UPLOADS=true
VITE_DISABLE_EMAIL_STATUS_CHECK=true # Allow test emailsNext Steps
- Environment Variables - Complete variable reference
- Customization - Customize UI text and labels
- With Quickback - How the compiler builds Account UI into your Worker