Preferences
Update user preferences such as theme and language.
User preferences control display settings that persist across sessions - theme (light/dark/system) and language selection. These settings are stored in cookies and apply immediately without page reload. The preferences page only shows options that are actually available: theme toggle appears when enabled in config, language selector appears when multiple locales are configured.
User preferences are display settings (theme, language) that persist across sessions and customize the application's appearance for each user.
- Location:
apps/web/app/[locale]/(internal)/settings/preferences/page.tsx - Route:
/settings/preferences
In the preferences page, users can update their preferences such as theme (if theme toggle is enabled) and language (if more than one language is supported).
The component that handles the preferences update is PreferencesContent - which is located in apps/web/app/[locale]/(internal)/settings/preferences/_components/preferences-content.tsx.
Theme Preferences
Theme preference supports three modes: light, dark, and system (follows OS preference). The preference is stored in a cookie and read in the root layout to prevent flash of wrong theme on page load.
How Theme Works
- User selects theme in preferences
- Cookie is set with the preference value
- Root layout reads cookie and applies the appropriate class to
<html> - Tailwind's dark mode (
dark:variants) activates based on the class
Enable/Disable Theme Toggle
Control theme toggle visibility in your feature flags config:
// config/feature-flags.config.tsexport const featureFlags = { enableThemeToggle: true, // Set to false to hide theme selector};Language Preferences
Language preference changes the URL locale segment and loads the corresponding translation files. The selector only appears when multiple languages are configured.
Configure Available Languages
Add languages in your i18n settings:
// lib/i18n/i18n.settings.tsexport const languages = ['en', 'es', 'de', 'fr'];export const defaultLanguage = 'en';Each language needs translation files in public/locales/[lang]/.
Adding Custom Preferences
To add custom preferences (timezone, notification settings, etc.), extend the preferences form:
- Add the preference field to your schema
- Create a form component for the new preference
- Add a server action to persist the preference
- Include the component in the preferences page
Example for a timezone preference:
// Custom preference componentexport function TimezonePreference() { const timezones = Intl.supportedValuesOf('timeZone'); return ( <Select onValueChange={handleTimezoneChange}> <SelectTrigger> <SelectValue placeholder="Select timezone" /> </SelectTrigger> <SelectContent> {timezones.map((tz) => ( <SelectItem key={tz} value={tz}>{tz}</SelectItem> ))} </SelectContent> </Select> );}Common Pitfalls
- Theme flash on page load: If theme preference isn't read early enough, users see a flash of the wrong theme. The preference is set via cookie and read in the root layout to prevent this.
- Language change without full reload: Changing language updates the URL locale segment and triggers navigation. Ensure your client components handle the transition gracefully.
- Showing disabled options: If theme toggle is disabled or only one language is available, the respective controls are hidden. Don't show grayed-out controls - it confuses users.
- Not persisting preferences for anonymous users: Preferences work via cookies before sign-in, but consider whether they should sync to the database after authentication.
Next: Authorization Policies →