User Settings Management#
User settings updates in Zerobyte flow through Better Auth's updateUser endpoint, which is automatically generated for any fields declared under user.additionalFields in the server auth config. The Settings UI calls this endpoint directly β no custom API route is needed for profile field changes.
Server: Additional Fields via Better Auth#
In app/server/lib/auth.ts, four custom fields are registered on the user model:
| Field | Type | Notes |
|---|---|---|
username | string | Required, returned in session |
hasDownloadedResticPassword | boolean | Tracks recovery key download |
dateFormat | string | User date display preference |
timeFormat | string | User time display preference |
Declaring a field here causes Better Auth to automatically expose it through the POST /api/auth/update-user endpoint β no additional server-side handler is required . The username and hasDownloadedResticPassword fields are marked returned: true, meaning they are included in session responses .
The user.modelName is set to "usersTable" , mapping to the schema definition which also stores dateFormat (default: "MM/DD/YYYY") and timeFormat (default: "12h") .
Client: authClient and inferAdditionalFields#
The client-side authClient is created with inferAdditionalFields<typeof auth>() , which pulls the additional field types from the server auth instance so that authClient.updateUser(...) is fully typed against those fields. Other plugins (usernameClient, adminClient, organizationClient, etc.) add their own methods to the same client object .
Settings UI: Auto-Save on Select Change#
Date/time format settings use an auto-save pattern β there is no explicit "Save" button. Instead, each <Select> fires onValueChange, which immediately calls authClient.updateUser(...).
These handlers are defined in the DateTimeFormatSection component:
handleDateFormatChangeβ called on date format select change; skips the API call if the value is unchanged.handleTimeFormatChangeβ same pattern for time format.- Both delegate to
handleDateTimeFormatChange, which callsauthClient.updateUser({ dateFormat, timeFormat, ... })and triggerswindow.location.reload()on success to apply the new preferences.
The component also displays a live preview of the currently selected format before saving, using the formatDateTime utility from useTimeFormat.
Read-Only Fields#
username and email are rendered as disabled inputs in the Account Information section β they cannot be changed from the Settings UI. Password changes go through authClient.changePassword(...) , which revokes other sessions on success and then automatically signs the user out.
Settings Page Structure#
The settings page at /(dashboard)/settings/ is rendered through settings-shell.tsx, which provides runtime-specific layouts:
- Desktop runtime β Renders a simplified settings view with only
DateTimeFormatSectionand (if permitted)RecoveryKeySection. Other settings sections (password, API keys, 2FA, organization tabs) are hidden in desktop mode. - Web runtime β Renders the full
SettingsPagecomponent with two tabs:- Account β Account information, date/time format, password change, backup recovery key download, API keys, 2FA, passkeys.
- Organization β Shown only to users with the
organizationSettings.viewpermission; contains org details, member management, and SSO configuration.
Tab state (in web mode) is persisted in the URL via the ?tab= search param.
Key Files#
| File | Purpose |
|---|---|
app/server/lib/auth.ts | Better Auth config β defines additional user fields |
app/client/lib/auth-client.ts | Client auth instance with inferAdditionalFields |
app/client/modules/settings/routes/settings-shell.tsx | Settings shell β runtime-specific layout wrapper |
app/client/modules/settings/routes/settings.tsx | Full settings page UI (web runtime) |
app/client/modules/settings/components/date-time-format-section.tsx | Date/time format UI and auto-save handlers |
app/server/db/schema.ts | DB schema with default values for dateFormat / timeFormat |